diff --git a/apier/v1/filterindexecache_it_test.go b/apier/v1/filterindexecache_it_test.go index 932b4fd4a..41d310997 100644 --- a/apier/v1/filterindexecache_it_test.go +++ b/apier/v1/filterindexecache_it_test.go @@ -567,6 +567,12 @@ func testV1FIdxCaGetStatQueuesWithNotFound(t *testing.T) { err.Error() != utils.ErrNotFound.Error() { t.Error(err) } + + tEv.CGREventWithOpts.CGREvent.Tenant = utils.EmptyString + if err := tFIdxCaRpc.Call(utils.StatSv1ProcessEvent, tEv, &reply); err == nil || + err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } } func testV1FIdxCaSetStatQueueProfile(t *testing.T) { diff --git a/apier/v1/stats.go b/apier/v1/stats.go index 6286e9eb6..41385d532 100644 --- a/apier/v1/stats.go +++ b/apier/v1/stats.go @@ -27,10 +27,14 @@ import ( // GetStatQueueProfile returns a StatQueue profile func (apierSv1 *APIerSv1) GetStatQueueProfile(arg *utils.TenantID, reply *engine.StatQueueProfile) (err error) { - if missing := utils.MissingStructFields(arg, []string{"Tenant", "ID"}); len(missing) != 0 { //Params missing + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } - if sCfg, err := apierSv1.DataManager.GetStatQueueProfile(arg.Tenant, arg.ID, + tnt := arg.Tenant + if tnt == utils.EmptyString { + tnt = apierSv1.Config.GeneralCfg().DefaultTenant + } + if sCfg, err := apierSv1.DataManager.GetStatQueueProfile(tnt, arg.ID, true, true, utils.NonTransactional); err != nil { return utils.APIErrorHandler(err) } else { @@ -41,10 +45,11 @@ func (apierSv1 *APIerSv1) GetStatQueueProfile(arg *utils.TenantID, reply *engine // GetStatQueueProfileIDs returns list of statQueueProfile IDs registered for a tenant func (apierSv1 *APIerSv1) GetStatQueueProfileIDs(args *utils.PaginatorWithTenant, stsPrfIDs *[]string) error { - if missing := utils.MissingStructFields(args, []string{utils.Tenant}); len(missing) != 0 { //Params missing - return utils.NewErrMandatoryIeMissing(missing...) + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = apierSv1.Config.GeneralCfg().DefaultTenant } - prfx := utils.StatQueueProfilePrefix + args.Tenant + ":" + prfx := utils.StatQueueProfilePrefix + tnt + ":" keys, err := apierSv1.DataManager.DataDB().GetKeysForPrefix(prfx) if err != nil { return err @@ -62,9 +67,12 @@ func (apierSv1 *APIerSv1) GetStatQueueProfileIDs(args *utils.PaginatorWithTenant // SetStatQueueProfile alters/creates a StatQueueProfile func (apierSv1 *APIerSv1) SetStatQueueProfile(arg *engine.StatQueueWithCache, reply *string) (err error) { - if missing := utils.MissingStructFields(arg.StatQueueProfile, []string{"Tenant", "ID"}); len(missing) != 0 { + if missing := utils.MissingStructFields(arg.StatQueueProfile, []string{utils.ID}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } + if arg.Tenant == utils.EmptyString { + arg.Tenant = apierSv1.Config.GeneralCfg().DefaultTenant + } if err = apierSv1.DataManager.SetStatQueueProfile(arg.StatQueueProfile, true); err != nil { return utils.APIErrorHandler(err) } @@ -99,18 +107,22 @@ func (apierSv1 *APIerSv1) SetStatQueueProfile(arg *engine.StatQueueWithCache, re // Remove a specific stat configuration func (apierSv1 *APIerSv1) RemoveStatQueueProfile(args *utils.TenantIDWithCache, reply *string) error { - if missing := utils.MissingStructFields(args, []string{"Tenant", "ID"}); len(missing) != 0 { //Params missing + if missing := utils.MissingStructFields(args, []string{utils.ID}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } - if err := apierSv1.DataManager.RemoveStatQueueProfile(args.Tenant, args.ID, utils.NonTransactional, true); err != nil { + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = apierSv1.Config.GeneralCfg().DefaultTenant + } + if err := apierSv1.DataManager.RemoveStatQueueProfile(tnt, args.ID, utils.NonTransactional, true); err != nil { return utils.APIErrorHandler(err) } //handle caching for StatQueueProfile - if err := apierSv1.CallCache(args.Cache, args.Tenant, utils.CacheStatQueueProfiles, - args.TenantID(), nil, nil, args.Opts); err != nil { + if err := apierSv1.CallCache(args.Cache, tnt, utils.CacheStatQueueProfiles, + utils.ConcatenatedKey(tnt, args.ID), nil, nil, args.Opts); err != nil { return utils.APIErrorHandler(err) } - if err := apierSv1.DataManager.RemoveStatQueue(args.Tenant, args.ID, utils.NonTransactional); err != nil { + if err := apierSv1.DataManager.RemoveStatQueue(tnt, args.ID, utils.NonTransactional); err != nil { return utils.APIErrorHandler(err) } //generate a loadID for CacheStatQueueProfiles and CacheStatQueues and store it in database @@ -120,8 +132,8 @@ func (apierSv1 *APIerSv1) RemoveStatQueueProfile(args *utils.TenantIDWithCache, return utils.APIErrorHandler(err) } //handle caching for StatQueues - if err := apierSv1.CallCache(args.Cache, args.Tenant, utils.CacheStatQueues, - args.TenantID(), nil, nil, args.Opts); err != nil { + if err := apierSv1.CallCache(args.Cache, tnt, utils.CacheStatQueues, + utils.ConcatenatedKey(tnt, args.ID), nil, nil, args.Opts); err != nil { return utils.APIErrorHandler(err) } *reply = utils.OK diff --git a/apier/v1/stats_it_test.go b/apier/v1/stats_it_test.go index 23c36089f..00b207b39 100644 --- a/apier/v1/stats_it_test.go +++ b/apier/v1/stats_it_test.go @@ -81,6 +81,8 @@ var ( testV1STSProcessMetricsWithFilter, testV1STSProcessStaticMetrics, testV1STSProcessStatWithThreshold, + testV1STSGetStatQueueProfileWithoutTenant, + testV1STSRemStatQueueProfileWithoutTenant, //testV1STSProcessCDRStat, testV1STSOverWriteStats, testV1STSProcessStatWithThreshold2, @@ -313,6 +315,13 @@ func testV1STSProcessEvent(t *testing.T) { t.Errorf("expecting: %+v, received reply: %+v", expectedFloatMetrics2, floatMetrics2) } + if err := stsV1Rpc.Call(utils.StatSv1GetQueueFloatMetrics, + &utils.TenantIDWithOpts{TenantID: &utils.TenantID{ID: "Stats1"}}, &floatMetrics2); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expectedFloatMetrics2, floatMetrics2) { + t.Errorf("expecting: %+v, received reply: %+v", expectedFloatMetrics2, floatMetrics2) + } + } func testV1STSGetStatsAfterRestart(t *testing.T) { @@ -434,6 +443,11 @@ func testV1STSSetStatQueueProfile(t *testing.T) { func testV1STSGetStatQueueProfileIDs(t *testing.T) { expected := []string{"Stats1", "TEST_PROFILE1"} var result []string + if err := stsV1Rpc.Call(utils.APIerSv1GetStatQueueProfileIDs, &utils.PaginatorWithTenant{}, &result); err != nil { + t.Error(err) + } else if len(expected) != len(result) { + t.Errorf("Expecting : %+v, received: %+v", expected, result) + } if err := stsV1Rpc.Call(utils.APIerSv1GetStatQueueProfileIDs, &utils.PaginatorWithTenant{Tenant: "cgrates.org"}, &result); err != nil { t.Error(err) } else if len(expected) != len(result) { @@ -1170,3 +1184,63 @@ func BenchmarkSTSV1GetQueueStringMetrics(b *testing.B) { } } } + +func testV1STSGetStatQueueProfileWithoutTenant(t *testing.T) { + statConfig := &engine.StatQueueWithCache{ + StatQueueProfile: &engine.StatQueueProfile{ + ID: "TEST_PROFILE10", + FilterIDs: []string{"FLTR_1"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + ExpiryTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }, + QueueLength: 10, + TTL: time.Duration(10) * time.Second, + Metrics: []*engine.MetricWithFilters{ + { + MetricID: utils.MetaACD, + }, + { + MetricID: utils.MetaTCD, + }, + }, + ThresholdIDs: []string{"Val1", "Val2"}, + Blocker: true, + Stored: true, + Weight: 20, + MinItems: 1, + }, + } + var result string + if err := stsV1Rpc.Call(utils.APIerSv1SetStatQueueProfile, statConfig, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + statConfig.Tenant = "cgrates.org" + var reply *engine.StatQueueProfile + if err := stsV1Rpc.Call(utils.APIerSv1GetStatQueueProfile, + &utils.TenantID{ID: "TEST_PROFILE10"}, + &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(reply, statConfig.StatQueueProfile) { + t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(statConfig.StatQueueProfile), utils.ToJSON(reply)) + } +} + +func testV1STSRemStatQueueProfileWithoutTenant(t *testing.T) { + var reply string + if err := stsV1Rpc.Call(utils.APIerSv1RemoveStatQueueProfile, + &utils.TenantIDWithCache{ID: "TEST_PROFILE10"}, + &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply returned", reply) + } + var result *engine.StatQueueProfile + if err := stsV1Rpc.Call(utils.APIerSv1GetStatQueueProfile, + &utils.TenantID{ID: "TEST_PROFILE10"}, + &result); err == nil || utils.ErrNotFound.Error() != err.Error() { + t.Error(err) + } +} diff --git a/dispatchers/stats_it_test.go b/dispatchers/stats_it_test.go index 6cbd09ad1..48c8d399f 100755 --- a/dispatchers/stats_it_test.go +++ b/dispatchers/stats_it_test.go @@ -153,6 +153,23 @@ func testDspStsGetStatFailover(t *testing.T) { t.Errorf("Expected error NOT_FOUND but recived %v and reply %v\n", err, reply) } allEngine2.startEngine(t) + + args2.Tenant = utils.EmptyString + allEngine.stopEngine(t) + if err := dispEngine.RPC.Call(utils.StatSv1GetQueueStringMetrics, + args2, &metrics); err != nil { + t.Error(err) + } + + allEngine.startEngine(t) + allEngine2.stopEngine(t) + + if err := dispEngine.RPC.Call(utils.StatSv1GetQueueStringMetrics, + args2, &metrics); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Errorf("Expected error NOT_FOUND but recived %v and reply %v\n", err, reply) + } + allEngine2.startEngine(t) + } func testDspStsPing(t *testing.T) { @@ -369,4 +386,28 @@ func testDspStsTestAuthKey3(t *testing.T) { } else if !reflect.DeepEqual(estats, reply) { t.Errorf("expecting: %+v, received reply: %v", estats, reply) } + + if err := dispEngine.RPC.Call(utils.StatSv1GetStatQueuesForEvent, + &engine.StatsArgsProcessEvent{ + CGREventWithOpts: &utils.CGREventWithOpts{ + CGREvent: &utils.CGREvent{ + ID: "GetStats", + Event: map[string]interface{}{ + utils.Account: "1002", + utils.AnswerTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + utils.Usage: time.Duration(45 * time.Second), + utils.RunID: utils.MetaDefault, + utils.COST: 10.0, + utils.Destination: "1001", + }, + }, + Opts: map[string]interface{}{ + utils.OptsAPIKey: "stat12345", + }, + }, + }, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(estats, reply) { + t.Errorf("expecting: %+v, received reply: %v", estats, reply) + } } diff --git a/engine/stats.go b/engine/stats.go index 61d21c5ec..942ba5127 100644 --- a/engine/stats.go +++ b/engine/stats.go @@ -150,7 +150,7 @@ func (sS *StatService) StoreStatQueue(sq *StatQueue) (err error) { } // matchingStatQueuesForEvent returns ordered list of matching resources which are active by the time of the call -func (sS *StatService) matchingStatQueuesForEvent(args *StatsArgsProcessEvent) (sqs StatQueues, err error) { +func (sS *StatService) matchingStatQueuesForEvent(tnt string, args *StatsArgsProcessEvent) (sqs StatQueues, err error) { evNm := utils.MapStorage{ utils.MetaReq: args.Event, utils.MetaOpts: args.Opts, @@ -161,7 +161,7 @@ func (sS *StatService) matchingStatQueuesForEvent(args *StatsArgsProcessEvent) ( sS.cgrcfg.StatSCfg().StringIndexedFields, sS.cgrcfg.StatSCfg().PrefixIndexedFields, sS.cgrcfg.StatSCfg().SuffixIndexedFields, - sS.dm, utils.CacheStatFilterIndexes, args.Tenant, + sS.dm, utils.CacheStatFilterIndexes, tnt, sS.cgrcfg.StatSCfg().IndexedSelects, sS.cgrcfg.StatSCfg().NestedFields, ) @@ -171,7 +171,7 @@ func (sS *StatService) matchingStatQueuesForEvent(args *StatsArgsProcessEvent) ( } sqs = make(StatQueues, 0, len(sqIDs)) for sqID := range sqIDs { - sqPrfl, err := sS.dm.GetStatQueueProfile(args.Tenant, sqID, true, true, utils.NonTransactional) + sqPrfl, err := sS.dm.GetStatQueueProfile(tnt, sqID, true, true, utils.NonTransactional) if err != nil { if err == utils.ErrNotFound { continue @@ -182,7 +182,7 @@ func (sS *StatService) matchingStatQueuesForEvent(args *StatsArgsProcessEvent) ( !sqPrfl.ActivationInterval.IsActiveAtTime(*args.Time) { // not active continue } - if pass, err := sS.filterS.Pass(args.Tenant, sqPrfl.FilterIDs, + if pass, err := sS.filterS.Pass(tnt, sqPrfl.FilterIDs, evNm); err != nil { return nil, err } else if !pass { @@ -263,8 +263,8 @@ func (attr *StatsArgsProcessEvent) Clone() *StatsArgsProcessEvent { // processEvent processes a new event, dispatching to matching queues // queues matching are also cached to speed up -func (sS *StatService) processEvent(args *StatsArgsProcessEvent) (statQueueIDs []string, err error) { - matchSQs, err := sS.matchingStatQueuesForEvent(args) +func (sS *StatService) processEvent(tnt string, args *StatsArgsProcessEvent) (statQueueIDs []string, err error) { + matchSQs, err := sS.matchingStatQueuesForEvent(tnt, args) if err != nil { return nil, err } @@ -287,7 +287,7 @@ func (sS *StatService) processEvent(args *StatsArgsProcessEvent) (statQueueIDs [ if err != nil { utils.Logger.Warning( fmt.Sprintf(" Queue: %s, ignoring event: %s, error: %s", - sq.TenantID(), args.TenantID(), err.Error())) + sq.TenantID(), utils.ConcatenatedKey(tnt, args.ID), err.Error())) withErrors = true } if sS.cgrcfg.StatSCfg().StoreInterval != 0 && sq.dirty != nil { // don't save @@ -352,13 +352,17 @@ func (sS *StatService) V1ProcessEvent(args *StatsArgsProcessEvent, reply *[]stri if args.CGREvent == nil { return utils.NewErrMandatoryIeMissing(utils.CGREventString) } - if missing := utils.MissingStructFields(args, []string{utils.Tenant, utils.ID}); len(missing) != 0 { //Params missing + if missing := utils.MissingStructFields(args, []string{utils.ID}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } else if args.Event == nil { return utils.NewErrMandatoryIeMissing(utils.Event) } + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = sS.cgrcfg.GeneralCfg().DefaultTenant + } var ids []string - if ids, err = sS.processEvent(args); err != nil { + if ids, err = sS.processEvent(tnt, args); err != nil { return } *reply = ids @@ -370,13 +374,17 @@ func (sS *StatService) V1GetStatQueuesForEvent(args *StatsArgsProcessEvent, repl if args.CGREvent == nil { return utils.NewErrMandatoryIeMissing(utils.CGREventString) } - if missing := utils.MissingStructFields(args, []string{utils.Tenant, utils.ID}); len(missing) != 0 { //Params missing + if missing := utils.MissingStructFields(args, []string{utils.ID}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } else if args.Event == nil { return utils.NewErrMandatoryIeMissing(utils.Event) } + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = sS.cgrcfg.GeneralCfg().DefaultTenant + } var sQs StatQueues - if sQs, err = sS.matchingStatQueuesForEvent(args); err != nil { + if sQs, err = sS.matchingStatQueuesForEvent(tnt, args); err != nil { return } ids := make([]string, len(sQs)) @@ -389,7 +397,11 @@ func (sS *StatService) V1GetStatQueuesForEvent(args *StatsArgsProcessEvent, repl // V1GetStatQueue returns a StatQueue object func (sS *StatService) V1GetStatQueue(args *utils.TenantIDWithOpts, reply *StatQueue) (err error) { - sq, err := sS.dm.GetStatQueue(args.Tenant, args.ID, true, true, "") + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = sS.cgrcfg.GeneralCfg().DefaultTenant + } + sq, err := sS.dm.GetStatQueue(tnt, args.ID, true, true, "") if err != nil { return err } @@ -399,10 +411,14 @@ func (sS *StatService) V1GetStatQueue(args *utils.TenantIDWithOpts, reply *StatQ // V1GetQueueStringMetrics returns the metrics of a Queue as string values func (sS *StatService) V1GetQueueStringMetrics(args *utils.TenantID, reply *map[string]string) (err error) { - if missing := utils.MissingStructFields(args, []string{utils.Tenant, utils.ID}); len(missing) != 0 { //Params missing + if missing := utils.MissingStructFields(args, []string{utils.ID}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } - sq, err := sS.dm.GetStatQueue(args.Tenant, args.ID, true, true, "") + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = sS.cgrcfg.GeneralCfg().DefaultTenant + } + sq, err := sS.dm.GetStatQueue(tnt, args.ID, true, true, "") if err != nil { if err != utils.ErrNotFound { err = utils.NewErrServerError(err) @@ -421,10 +437,14 @@ func (sS *StatService) V1GetQueueStringMetrics(args *utils.TenantID, reply *map[ // V1GetQueueFloatMetrics returns the metrics as float64 values func (sS *StatService) V1GetQueueFloatMetrics(args *utils.TenantID, reply *map[string]float64) (err error) { - if missing := utils.MissingStructFields(args, []string{utils.Tenant, utils.ID}); len(missing) != 0 { //Params missing + if missing := utils.MissingStructFields(args, []string{utils.ID}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } - sq, err := sS.dm.GetStatQueue(args.Tenant, args.ID, true, true, "") + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = sS.cgrcfg.GeneralCfg().DefaultTenant + } + sq, err := sS.dm.GetStatQueue(tnt, args.ID, true, true, "") if err != nil { if err != utils.ErrNotFound { err = utils.NewErrServerError(err) diff --git a/engine/stats_test.go b/engine/stats_test.go index 1bb87b333..26dede3f6 100644 --- a/engine/stats_test.go +++ b/engine/stats_test.go @@ -250,7 +250,7 @@ func TestStatQueuesPopulateStatsService(t *testing.T) { } func TestStatQueuesMatchingStatQueuesForEvent(t *testing.T) { - msq, err := statService.matchingStatQueuesForEvent(statsEvs[0]) + msq, err := statService.matchingStatQueuesForEvent(statsEvs[0].Tenant, statsEvs[0]) if err != nil { t.Errorf("Error: %+v", err) } @@ -261,7 +261,7 @@ func TestStatQueuesMatchingStatQueuesForEvent(t *testing.T) { } else if !reflect.DeepEqual(stqs[0].sqPrfl, msq[0].sqPrfl) { t.Errorf("Expecting: %+v, received: %+v", stqs[0].sqPrfl, msq[0].sqPrfl) } - msq, err = statService.matchingStatQueuesForEvent(statsEvs[1]) + msq, err = statService.matchingStatQueuesForEvent(statsEvs[1].Tenant, statsEvs[1]) if err != nil { t.Errorf("Error: %+v", err) } @@ -272,7 +272,7 @@ func TestStatQueuesMatchingStatQueuesForEvent(t *testing.T) { } else if !reflect.DeepEqual(stqs[1].sqPrfl, msq[0].sqPrfl) { t.Errorf("Expecting: %+v, received: %+v", stqs[1].sqPrfl, msq[0].sqPrfl) } - msq, err = statService.matchingStatQueuesForEvent(statsEvs[2]) + msq, err = statService.matchingStatQueuesForEvent(statsEvs[2].Tenant, statsEvs[2]) if err != nil { t.Errorf("Error: %+v", err) } @@ -327,7 +327,7 @@ func TestStatQueuesProcessEvent(t *testing.T) { func TestStatQueuesMatchWithIndexFalse(t *testing.T) { statService.cgrcfg.StatSCfg().IndexedSelects = false - msq, err := statService.matchingStatQueuesForEvent(statsEvs[0]) + msq, err := statService.matchingStatQueuesForEvent(statsEvs[0].Tenant, statsEvs[0]) if err != nil { t.Errorf("Error: %+v", err) } @@ -338,7 +338,7 @@ func TestStatQueuesMatchWithIndexFalse(t *testing.T) { } else if !reflect.DeepEqual(stqs[0].sqPrfl, msq[0].sqPrfl) { t.Errorf("Expecting: %+v, received: %+v", stqs[0].sqPrfl, msq[0].sqPrfl) } - msq, err = statService.matchingStatQueuesForEvent(statsEvs[1]) + msq, err = statService.matchingStatQueuesForEvent(statsEvs[1].Tenant, statsEvs[1]) if err != nil { t.Errorf("Error: %+v", err) } @@ -349,7 +349,7 @@ func TestStatQueuesMatchWithIndexFalse(t *testing.T) { } else if !reflect.DeepEqual(stqs[1].sqPrfl, msq[0].sqPrfl) { t.Errorf("Expecting: %+v, received: %+v", stqs[1].sqPrfl, msq[0].sqPrfl) } - msq, err = statService.matchingStatQueuesForEvent(statsEvs[2]) + msq, err = statService.matchingStatQueuesForEvent(statsEvs[2].Tenant, statsEvs[2]) if err != nil { t.Errorf("Error: %+v", err) }