From 6887863ed1045ffc4df3a6f60cf761907e613e59 Mon Sep 17 00:00:00 2001 From: TeoV Date: Wed, 6 Jan 2021 13:55:52 +0200 Subject: [PATCH] Update encoding for Get/Set AccountProfile for DataManager --- accounts/abstractbalance.go | 6 +- accounts/concretebalance.go | 12 +- accounts/concretebalance_test.go | 2 +- apier/v1/accountprofiles.go | 8 - apier/v1/accountprofiles_it_test.go | 346 ++++++++++++++++++++ data/conf/samples/tutinternal/cgrates.json | 5 + data/conf/samples/tutmongo/cgrates.json | 5 + data/conf/samples/tutmongojson/cgrates.json | 150 +++++++-- data/conf/samples/tutmysql/cgrates.json | 1 + data/conf/samples/tutmysqljson/cgrates.json | 147 +++++++-- engine/datamanager.go | 27 -- engine/model_helpers.go | 26 +- engine/storage_mongo_datadb.go | 39 ++- engine/tpreader.go | 1 - loaders/loader.go | 2 - utils/accountprofile.go | 40 +-- utils/accountprofile_test.go | 30 +- utils/consts.go | 11 +- utils/decimal.go | 52 ++- 19 files changed, 752 insertions(+), 158 deletions(-) create mode 100644 apier/v1/accountprofiles_it_test.go diff --git a/accounts/abstractbalance.go b/accounts/abstractbalance.go index 65e58f2f0..e6061f24f 100644 --- a/accounts/abstractbalance.go +++ b/accounts/abstractbalance.go @@ -55,8 +55,8 @@ func (aB *abstractBalance) costIncrement(tnt string, ev utils.DataProvider) (cos } // nothing matched, return default costIcrm = &utils.CostIncrement{ - Increment: decimal.New(1, 0), - RecurrentFee: decimal.New(-1, 0)} + Increment: &utils.Decimal{decimal.New(1, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(-1, 0)}} return } @@ -74,7 +74,7 @@ func (aB *abstractBalance) unitFactor(tnt string, ev utils.DataProvider) (uF *ut } // nothing matched, return default uF = &utils.UnitFactor{ - Factor: decimal.New(1, 0), + Factor: &utils.Decimal{decimal.New(1, 0)}, } return } diff --git a/accounts/concretebalance.go b/accounts/concretebalance.go index d4e5f4d46..86c5492a9 100644 --- a/accounts/concretebalance.go +++ b/accounts/concretebalance.go @@ -55,8 +55,8 @@ func (cB *concreteBalance) costIncrement(tnt string, ev utils.DataProvider) (cos } // nothing matched, return default costIcrm = &utils.CostIncrement{ - Increment: decimal.New(1, 0), - RecurrentFee: decimal.New(-1, 0)} + Increment: &utils.Decimal{decimal.New(1, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(-1, 0)}} return } @@ -74,7 +74,7 @@ func (cB *concreteBalance) unitFactor(tnt string, ev utils.DataProvider) (uF *ut } // nothing matched, return default uF = &utils.UnitFactor{ - Factor: decimal.New(1, 0), + Factor: &utils.Decimal{decimal.New(1, 0)}, } return } @@ -137,8 +137,8 @@ func (cB *concreteBalance) debitUnits(dUnts *decimal.Big, incrm *decimal.Big, var hasUF bool if uF.Factor.Cmp(decimal.New(1, 0)) != 0 { - dUnts = utils.MultiplyBig(dUnts, uF.Factor) - incrm = utils.MultiplyBig(incrm, uF.Factor) + dUnts = utils.MultiplyBig(dUnts, uF.Factor.Big) + incrm = utils.MultiplyBig(incrm, uF.Factor.Big) hasUF = true } @@ -162,7 +162,7 @@ func (cB *concreteBalance) debitUnits(dUnts *decimal.Big, incrm *decimal.Big, rmain = utils.AddBig(rmain, blncLmt) } if hasUF { - dbted = utils.DivideBig(dUnts, uF.Factor) + dbted = utils.DivideBig(dUnts, uF.Factor.Big) } else { dbted = dUnts } diff --git a/accounts/concretebalance_test.go b/accounts/concretebalance_test.go index bbe4b3fe6..9a87ba91f 100644 --- a/accounts/concretebalance_test.go +++ b/accounts/concretebalance_test.go @@ -38,7 +38,7 @@ func TestCBDebitUnits(t *testing.T) { }, UnitFactors: []*utils.UnitFactor{ { - Factor: decimal.New(100, 0), // EuroCents + Factor: &utils.Decimal{decimal.New(100, 0)}, // EuroCents }, }, Value: 500, // 500 EURcents diff --git a/apier/v1/accountprofiles.go b/apier/v1/accountprofiles.go index d39fc8d78..941ba1e2f 100644 --- a/apier/v1/accountprofiles.go +++ b/apier/v1/accountprofiles.go @@ -108,10 +108,6 @@ func (apierSv1 *APIerSv1) SetAccountProfile(ap *AccountProfileWithCache, reply * if err := apierSv1.DataManager.SetLoadIDs(map[string]int64{utils.CacheAccountProfiles: time.Now().UnixNano()}); err != nil { return utils.APIErrorHandler(err) } - if err := apierSv1.CallCache(ap.Cache, ap.Tenant, utils.CacheAccountProfiles, - ap.TenantID(), &ap.FilterIDs, nil, ap.Opts); err != nil { - return utils.APIErrorHandler(err) - } *reply = utils.OK return nil } @@ -133,10 +129,6 @@ func (apierSv1 *APIerSv1) RemoveAccountProfile(arg *utils.TenantIDWithCache, rep if err := apierSv1.DataManager.SetLoadIDs(map[string]int64{utils.CacheAccountProfiles: time.Now().UnixNano()}); err != nil { return utils.APIErrorHandler(err) } - if err := apierSv1.CallCache(arg.Cache, tnt, utils.CacheAccountProfiles, - utils.ConcatenatedKey(tnt, arg.ID), nil, nil, arg.Opts); err != nil { - return utils.APIErrorHandler(err) - } *reply = utils.OK return nil } diff --git a/apier/v1/accountprofiles_it_test.go b/apier/v1/accountprofiles_it_test.go new file mode 100644 index 000000000..f0ec8bf0e --- /dev/null +++ b/apier/v1/accountprofiles_it_test.go @@ -0,0 +1,346 @@ +// +build integration + +/* +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 v1 + +import ( + "net/rpc" + "path" + "reflect" + "testing" + "time" + + "github.com/ericlagergren/decimal" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +var ( + accPrfCfgPath string + accPrfCfg *config.CGRConfig + accSRPC *rpc.Client + accPrfDataDir = "/usr/share/cgrates" + accPrf *AccountProfileWithCache + accPrfConfigDIR string //run tests for specific configuration + + sTestsAccPrf = []func(t *testing.T){ + testAccountSInitCfg, + testAccountSInitDataDb, + testAccountSResetStorDb, + testAccountSStartEngine, + testAccountSRPCConn, + testAccountSLoadFromFolder, + testAccountSGetAccountProfile, + testAccountSPing, + testAccountSSettAccountProfile, + testAccountSGetAccountProfileIDs, + testAccountSGetAccountProfileIDsCount, + testAccountSUpdateAccountProfile, + testAccountSRemoveAccountProfile, + testAccountSKillEngine, + } +) + +//Test start here +func TestAccountSIT(t *testing.T) { + switch *dbType { + case utils.MetaInternal: + accPrfConfigDIR = "tutinternal" + case utils.MetaMySQL: + accPrfConfigDIR = "tutmysql" + case utils.MetaMongo: + accPrfConfigDIR = "tutmongo" + case utils.MetaPostgres: + t.SkipNow() + default: + t.Fatal("Unknown Database type") + } + for _, stest := range sTestsAccPrf { + t.Run(accPrfConfigDIR, stest) + } +} + +func testAccountSInitCfg(t *testing.T) { + var err error + accPrfCfgPath = path.Join(accPrfDataDir, "conf", "samples", accPrfConfigDIR) + accPrfCfg, err = config.NewCGRConfigFromPath(accPrfCfgPath) + if err != nil { + t.Error(err) + } +} + +func testAccountSInitDataDb(t *testing.T) { + if err := engine.InitDataDb(accPrfCfg); err != nil { + t.Fatal(err) + } +} + +// Wipe out the cdr database +func testAccountSResetStorDb(t *testing.T) { + if err := engine.InitStorDb(accPrfCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine +func testAccountSStartEngine(t *testing.T) { + if _, err := engine.StopStartEngine(accPrfCfgPath, *waitRater); err != nil { + t.Fatal(err) + } +} + +// Connect rpc client to rater +func testAccountSRPCConn(t *testing.T) { + var err error + accSRPC, err = newRPCClient(accPrfCfg.ListenCfg()) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } +} + +func testAccountSLoadFromFolder(t *testing.T) { + var reply string + acts := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutaccounts")} + if err := accSRPC.Call(utils.APIerSv1LoadTariffPlanFromFolder, acts, &reply); err != nil { + t.Error(err) + } + time.Sleep(100 * time.Millisecond) +} + +func testAccountSGetAccountProfile(t *testing.T) { + expected := &utils.AccountProfile{ + Tenant: "cgrates.org", + ID: "1001", + FilterIDs: []string{}, + Weight: 20, + Balances: []*utils.Balance{ + &utils.Balance{ + ID: "MonetaryBalance", + FilterIDs: []string{}, + Weight: 10, + Type: utils.MONETARY, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"fltr1", "fltr2"}, + Increment: &utils.Decimal{decimal.New(13, 1)}, + FixedFee: &utils.Decimal{decimal.New(23, 1)}, + RecurrentFee: &utils.Decimal{decimal.New(33, 1)}, + }, + }, + CostAttributes: []string{"attr1", "attr2"}, + UnitFactors: []*utils.UnitFactor{ + &utils.UnitFactor{ + FilterIDs: []string{"fltr1", "fltr2"}, + Factor: &utils.Decimal{decimal.New(100, 0)}, + }, + &utils.UnitFactor{ + FilterIDs: []string{"fltr3"}, + Factor: &utils.Decimal{decimal.New(200, 0)}, + }, + }, + Value: 14, + }, + &utils.Balance{ + ID: "VoiceBalance", + FilterIDs: []string{}, + Weight: 10, + Type: utils.VOICE, + CostIncrements: []*utils.CostIncrement{}, + CostAttributes: []string{}, + UnitFactors: []*utils.UnitFactor{}, + Value: 3600000000000, + }, + }, + ThresholdIDs: []string{utils.META_NONE}, + } + if *encoding == utils.MetaGOB { + expected.FilterIDs = nil + for i := range expected.Balances { + expected.Balances[i].FilterIDs = nil + } + } + var reply *utils.AccountProfile + if err := accSRPC.Call(utils.APIerSv1GetAccountProfile, + utils.TenantIDWithOpts{TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "1001"}}, &reply); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(expected, reply) { + t.Errorf("Expecting : %+v \n received: %+v", utils.ToJSON(expected), utils.ToJSON(reply)) + } +} + +func testAccountSPing(t *testing.T) { + var resp string + if err := accSRPC.Call(utils.AccountSv1Ping, new(utils.CGREventWithOpts), &resp); err != nil { + t.Error(err) + } else if resp != utils.Pong { + t.Error("Unexpected reply returned", resp) + } +} + +func testAccountSSettAccountProfile(t *testing.T) { + accPrf = &AccountProfileWithCache{ + AccountProfileWithOpts: &utils.AccountProfileWithOpts{ + AccountProfile: &utils.AccountProfile{ + Tenant: "cgrates.org", + ID: "id_test", + Weight: 10, + Balances: []*utils.Balance{ + &utils.Balance{ + ID: "MonetaryBalance", + FilterIDs: []string{}, + Weight: 10, + Type: utils.MONETARY, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"fltr1", "fltr2"}, + Increment: &utils.Decimal{decimal.New(13, 1)}, + FixedFee: &utils.Decimal{decimal.New(23, 1)}, + RecurrentFee: &utils.Decimal{decimal.New(33, 1)}, + }, + }, + CostAttributes: []string{"attr1", "attr2"}, + UnitFactors: []*utils.UnitFactor{ + &utils.UnitFactor{ + FilterIDs: []string{"fltr1", "fltr2"}, + Factor: &utils.Decimal{decimal.New(100, 0)}, + }, + &utils.UnitFactor{ + FilterIDs: []string{"fltr3"}, + Factor: &utils.Decimal{decimal.New(200, 0)}, + }, + }, + Value: 14, + }, + &utils.Balance{ + ID: "VoiceBalance", + FilterIDs: []string{}, + Weight: 10, + Type: utils.VOICE, + CostIncrements: []*utils.CostIncrement{}, + CostAttributes: []string{}, + UnitFactors: []*utils.UnitFactor{}, + Value: 3600000000000, + }, + }, + ThresholdIDs: []string{utils.META_NONE}, + }, + Opts: map[string]interface{}{}, + }, + } + var result string + expErr := utils.ErrNotFound.Error() + if err := accSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "id_test"}}, &result); err == nil || err.Error() != expErr { + t.Errorf("Expected error: %v received: %v", expErr, err) + } + var reply string + if err := accSRPC.Call(utils.APIerSv1SetAccountProfile, accPrf, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply returned", reply) + } + var reply2 *utils.AccountProfile + if err := accSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "id_test"}}, &reply2); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(accPrf.AccountProfile, reply2) { + t.Errorf("Expecting : %+v, received: %+v", accPrf.AccountProfile, reply2) + } + +} + +func testAccountSGetAccountProfileIDs(t *testing.T) { + expected := []string{"id_test", "1001"} + var result []string + if err := accSRPC.Call(utils.APIerSv1GetAccountProfileIDs, utils.PaginatorWithTenant{}, &result); err != nil { + t.Error(err) + } else if len(expected) != len(result) { + t.Errorf("Expecting : %+v, received: %+v", expected, result) + } + if err := accSRPC.Call(utils.APIerSv1GetAccountProfileIDs, utils.PaginatorWithTenant{Tenant: "cgrates.org"}, &result); err != nil { + t.Error(err) + } else if len(expected) != len(result) { + t.Errorf("Expecting : %+v, received: %+v", expected, result) + } + if err := accSRPC.Call(utils.APIerSv1GetAccountProfileIDs, utils.PaginatorWithTenant{ + Tenant: "cgrates.org", + Paginator: utils.Paginator{Limit: utils.IntPointer(1)}, + }, &result); err != nil { + t.Error(err) + } else if 1 != len(result) { + t.Errorf("Expecting : %+v, received: %+v", 1, result) + } + +} + +func testAccountSGetAccountProfileIDsCount(t *testing.T) { + var reply int + if err := accSRPC.Call(utils.APIerSv1GetAccountProfileIDsCount, + &utils.TenantWithOpts{Tenant: "cgrates.org"}, &reply); err != nil { + t.Error(err) + } else if reply != 2 { + t.Errorf("Expecting: 2, received: %+v", reply) + } + +} + +func testAccountSUpdateAccountProfile(t *testing.T) { + var reply string + accPrf.Weight = 2 + accPrf.Balances[0].CostIncrements[0].FixedFee = &utils.Decimal{decimal.New(1234, 1)} + if err := accSRPC.Call(utils.APIerSv1SetAccountProfile, accPrf, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply returned", reply) + } + var reply2 *utils.AccountProfile + if err := accSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "id_test"}}, &reply2); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(accPrf.AccountProfile, reply2) { + t.Errorf("Expecting : %+v, received: %+v", accPrf.AccountProfile, reply2) + } +} + +func testAccountSRemoveAccountProfile(t *testing.T) { + var reply string + if err := accSRPC.Call(utils.APIerSv1RemoveAccountProfile, &utils.TenantIDWithCache{Tenant: "cgrates.org", ID: "id_test"}, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply returned", reply) + } + var reply2 *utils.AccountProfile + expErr := utils.ErrNotFound.Error() + if err := accSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "id_test"}}, &reply2); err == nil || err.Error() != expErr { + t.Errorf("Expected error: %v received: %v", expErr, err) + } + if err := accSRPC.Call(utils.APIerSv1RemoveAccountProfile, &utils.TenantIDWithCache{Tenant: "cgrates.org", ID: "id_test"}, &reply2); err == nil || err.Error() != expErr { + t.Errorf("Expected error: %v received: %v", expErr, err) + } +} + +func testAccountSKillEngine(t *testing.T) { + if err := engine.KillEngine(100); err != nil { + t.Error(err) + } +} diff --git a/data/conf/samples/tutinternal/cgrates.json b/data/conf/samples/tutinternal/cgrates.json index 2928b145b..d48ae4a0b 100644 --- a/data/conf/samples/tutinternal/cgrates.json +++ b/data/conf/samples/tutinternal/cgrates.json @@ -115,6 +115,11 @@ }, +"accounts": { + "enabled": true +}, + + "filters": { "apiers_conns": ["*internal"] } diff --git a/data/conf/samples/tutmongo/cgrates.json b/data/conf/samples/tutmongo/cgrates.json index 6542a1189..fcfc6f076 100644 --- a/data/conf/samples/tutmongo/cgrates.json +++ b/data/conf/samples/tutmongo/cgrates.json @@ -125,6 +125,11 @@ }, +"accounts": { + "enabled": true + }, + + "filters": { "apiers_conns": ["*internal"], }, diff --git a/data/conf/samples/tutmongojson/cgrates.json b/data/conf/samples/tutmongojson/cgrates.json index 430f0d77e..699bf98ef 100644 --- a/data/conf/samples/tutmongojson/cgrates.json +++ b/data/conf/samples/tutmongojson/cgrates.json @@ -1,33 +1,139 @@ { -// CGRateS Configuration file + // CGRateS Configuration file -"general": { - "log_level": 7, - "reply_timeout": "30s", - "dbdata_encoding": "json", -}, + "general": { + "log_level": 7, + "dbdata_encoding": "json", + "reply_timeout": "30s", + }, -"listen": { - "rpc_json": ":2012", - "rpc_gob": ":2013", - "http": ":2080", -}, + "listen": { + "rpc_json": ":2012", + "rpc_gob": ":2013", + "http": ":2080", + }, -"data_db": { - "db_type": "mongo", - "db_name": "11", - "db_port": 27017, -}, + "data_db": { + "db_type": "mongo", + "db_name": "11", + "db_port": 27017, + }, -"stor_db": { - "db_type": "mongo", - "db_name": "cgrates2", - "db_port": 27017, -}, + "stor_db": { + "db_type": "mongo", + "db_name": "cgrates2", + "db_port": 27017, + }, -} \ No newline at end of file + "rals": { + "enabled": true, + "thresholds_conns": ["*internal"], + "max_increments":3000000, + }, + + + "schedulers": { + "enabled": true, + "cdrs_conns": ["*localhost"], + "stats_conns": ["*localhost"], + }, + + + "cdrs": { + "enabled": true, + }, + + + "chargers": { + "enabled": true, + "attributes_conns": ["*internal"], + }, + + + "resources": { + "enabled": true, + "store_interval": "1s", + "thresholds_conns": ["*internal"] + }, + + + "stats": { + "enabled": true, + "store_interval": "1s", + "thresholds_conns": ["*internal"], + }, + + + "thresholds": { + "enabled": true, + "store_interval": "1s", + }, + + + "routes": { + "enabled": true, + "stats_conns": ["*localhost"], + "resources_conns": ["*localhost"], + "rals_conns": ["*internal"], + }, + + + "attributes": { + "enabled": true, + "stats_conns": ["*localhost"], + "resources_conns": ["*localhost"], + "apiers_conns": ["*localhost"] + }, + + + "sessions": { + "enabled": true, + "rals_conns": ["*internal"], + "cdrs_conns": ["*internal"], + "chargers_conns": ["*internal"], + }, + + + "migrator": { + "out_datadb_type": "mongo", + "out_datadb_port": "27017", + "out_datadb_name": "10", + "out_stordb_type": "mongo", + "out_stordb_port": "27017", + "out_stordb_name": "cgrates", + "users_filters":["Account"], + }, + + + "apiers": { + "enabled": true, + "scheduler_conns": ["*internal"], + }, + + + "rates": { + "enabled": true + }, + + + "actions": { + "enabled": true + }, + + + "accounts": { + "enabled": true + }, + + + "filters": { + "apiers_conns": ["*internal"], + }, + + +} diff --git a/data/conf/samples/tutmysql/cgrates.json b/data/conf/samples/tutmysql/cgrates.json index 49dfdd1b0..d0014bfba 100644 --- a/data/conf/samples/tutmysql/cgrates.json +++ b/data/conf/samples/tutmysql/cgrates.json @@ -5,6 +5,7 @@ "general": { "log_level": 7, + //"dbdata_encoding": "*json", "reply_timeout": "50s", }, diff --git a/data/conf/samples/tutmysqljson/cgrates.json b/data/conf/samples/tutmysqljson/cgrates.json index 10993496d..6aa731592 100644 --- a/data/conf/samples/tutmysqljson/cgrates.json +++ b/data/conf/samples/tutmysqljson/cgrates.json @@ -1,30 +1,135 @@ { -// CGRateS Configuration file -// + // CGRateS Configuration file -"general": { - "log_level": 7, - "dbdata_encoding": "json", // encoding used to store object data in strings: -}, + "general": { + "log_level": 7, + "dbdata_encoding": "json", + "reply_timeout": "50s", + }, -"listen": { - "rpc_json": ":2012", - "rpc_gob": ":2013", - "http": ":2080", -}, + "listen": { + "rpc_json": ":2012", + "rpc_gob": ":2013", + "http": ":2080", + }, -"data_db": { // database used to store runtime data (eg: accounts, cdr stats) - "db_type": "redis", // data_db type: - "db_port": 6379, // data_db port to reach the database - "db_name": "11", // data_db database name to connect to - -}, + "data_db": { // database used to store runtime data (eg: accounts, cdr stats) + "db_type": "redis", // data_db type: + "db_port": 6379, // data_db port to reach the database + "db_name": "11", // data_db database name to connect to + }, -"stor_db": { - "db_password": "CGRateS.org", -}, + "stor_db": { + "db_password": "CGRateS.org", + }, -} \ No newline at end of file + "rals": { + "enabled": true, + "thresholds_conns": ["*internal"], + "max_increments":3000000, + }, + + + "schedulers": { + "enabled": true, + "cdrs_conns": ["*internal"], + "stats_conns": ["*localhost"], + }, + + + "cdrs": { + "enabled": true, + "chargers_conns":["*internal"], + }, + + + "attributes": { + "enabled": true, + "stats_conns": ["*localhost"], + "resources_conns": ["*localhost"], + "apiers_conns": ["*localhost"] + }, + + + "chargers": { + "enabled": true, + "attributes_conns": ["*internal"], + }, + + + "resources": { + "enabled": true, + "store_interval": "1s", + "thresholds_conns": ["*internal"] + }, + + + "stats": { + "enabled": true, + "store_interval": "1s", + "thresholds_conns": ["*internal"], + }, + + "thresholds": { + "enabled": true, + "store_interval": "1s", + }, + + + "routes": { + "enabled": true, + "prefix_indexed_fields":["*req.Destination"], + "stats_conns": ["*internal"], + "resources_conns": ["*internal"], + "rals_conns": ["*internal"], + }, + + + "sessions": { + "enabled": true, + "routes_conns": ["*internal"], + "resources_conns": ["*internal"], + "attributes_conns": ["*internal"], + "rals_conns": ["*internal"], + "cdrs_conns": ["*internal"], + "chargers_conns": ["*internal"], + }, + + + "migrator":{ + "out_stordb_password": "CGRateS.org", + "users_filters":["Account"], + }, + + + + "apiers": { + "enabled": true, + "scheduler_conns": ["*internal"], + }, + + + "rates": { + "enabled": true + }, + + + "actions": { + "enabled": true + }, + + + "accounts": { + "enabled": true + }, + + + "filters": { + "apiers_conns": ["*internal"], + }, + + +} diff --git a/engine/datamanager.go b/engine/datamanager.go index 5fa9cdfa4..6cdc28bb2 100644 --- a/engine/datamanager.go +++ b/engine/datamanager.go @@ -79,7 +79,6 @@ var ( utils.RateFilterIndexPrfx: {}, utils.FilterIndexPrfx: {}, utils.MetaAPIBan: {}, // not realy a prefix as this is not stored in DB - utils.AccountProfilePrefix: {}, } ) @@ -231,9 +230,6 @@ func (dm *DataManager) CacheDataFromDB(prfx string, ids []string, mustBeCached b case utils.ActionProfilePrefix: tntID := utils.NewTenantID(dataID) _, err = dm.GetActionProfile(tntID.Tenant, tntID.ID, false, true, utils.NonTransactional) - case utils.AccountProfilePrefix: - tntID := utils.NewTenantID(dataID) - _, err = dm.GetAccountProfile(tntID.Tenant, tntID.ID, false, true, utils.NonTransactional) case utils.AttributeFilterIndexes: var tntCtx, idxKey string if tntCtx, idxKey, err = splitFilterIndex(dataID); err != nil { @@ -3816,15 +3812,6 @@ func (dm *DataManager) checkFilters(tenant string, ids []string) (brokenReferenc func (dm *DataManager) GetAccountProfile(tenant, id string, cacheRead, cacheWrite bool, transactionID string) (ap *utils.AccountProfile, err error) { - tntID := utils.ConcatenatedKey(tenant, id) - if cacheRead { - if x, ok := Cache.Get(utils.CacheAccountProfiles, tntID); ok { - if x == nil { - return nil, utils.ErrNotFound - } - return x.(*utils.AccountProfile), nil - } - } if dm == nil { err = utils.ErrNoDatabaseConn return @@ -3844,23 +3831,9 @@ func (dm *DataManager) GetAccountProfile(tenant, id string, cacheRead, cacheWrit } } if err != nil { - err = utils.CastRPCErr(err) - if err == utils.ErrNotFound && cacheWrite { - if errCh := Cache.Set(utils.CacheAccountProfiles, tntID, nil, nil, - cacheCommit(transactionID), transactionID); errCh != nil { - return nil, errCh - } - - } return nil, err } } - if cacheWrite { - if errCh := Cache.Set(utils.CacheAccountProfiles, tntID, ap, nil, - cacheCommit(transactionID), transactionID); errCh != nil { - return nil, errCh - } - } return } diff --git a/engine/model_helpers.go b/engine/model_helpers.go index 8a3a9d044..2e615db56 100644 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -27,8 +27,6 @@ import ( "strings" "time" - "github.com/ericlagergren/decimal" - "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" ) @@ -3745,10 +3743,22 @@ func APItoAccountProfile(tpAp *utils.TPAccountProfile, timezone string) (ap *uti ap.Balances[i].CostIncrements = make([]*utils.CostIncrement, len(bal.CostIncrement)) for j, costIncrement := range bal.CostIncrement { ap.Balances[i].CostIncrements[j] = &utils.CostIncrement{ - FilterIDs: costIncrement.FilterIDs, - Increment: new(decimal.Big).SetFloat64(*costIncrement.Increment), - FixedFee: new(decimal.Big).SetFloat64(*costIncrement.FixedFee), - RecurrentFee: new(decimal.Big).SetFloat64(*costIncrement.RecurrentFee), + FilterIDs: costIncrement.FilterIDs, + } + if costIncrement.Increment != nil { + if ap.Balances[i].CostIncrements[j].Increment, err = utils.NewDecimalFromFloat64(*costIncrement.Increment); err != nil { + return + } + } + if costIncrement.FixedFee != nil { + if ap.Balances[i].CostIncrements[j].FixedFee, err = utils.NewDecimalFromFloat64(*costIncrement.FixedFee); err != nil { + return + } + } + if costIncrement.RecurrentFee != nil { + if ap.Balances[i].CostIncrements[j].RecurrentFee, err = utils.NewDecimalFromFloat64(*costIncrement.RecurrentFee); err != nil { + return + } } } } @@ -3763,7 +3773,9 @@ func APItoAccountProfile(tpAp *utils.TPAccountProfile, timezone string) (ap *uti for j, unitFactor := range bal.UnitFactors { ap.Balances[i].UnitFactors[j] = &utils.UnitFactor{ FilterIDs: unitFactor.FilterIDs, - Factor: new(decimal.Big).SetFloat64(unitFactor.Factor), + } + if ap.Balances[i].UnitFactors[j].Factor, err = utils.NewDecimalFromFloat64(unitFactor.Factor); err != nil { + return } } } diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index 69340d4c4..2771a8606 100644 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -29,6 +29,8 @@ import ( "sync" "time" + "github.com/ericlagergren/decimal" + "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/guardian" "github.com/cgrates/cgrates/utils" @@ -103,7 +105,8 @@ var ( CostLow = strings.ToLower(utils.COST) CostSourceLow = strings.ToLower(utils.CostSource) - tTime = reflect.TypeOf(time.Time{}) + tTime = reflect.TypeOf(time.Time{}) + decimalType = reflect.TypeOf(utils.Decimal{}) ) func TimeDecodeValue1(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { @@ -123,6 +126,33 @@ func TimeDecodeValue1(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val ref return nil } +func DecimalEncoder(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if val.Kind() != reflect.Struct { + return bsoncodec.ValueEncoderError{Name: "DecimalEncoder", Kinds: []reflect.Kind{reflect.Struct}, Received: val} + } + d, ok := val.Interface().(utils.Decimal) + if !ok { + return fmt.Errorf("cannot cast <%+v> to ", val.Interface()) + } + return vw.WriteString(d.String()) +} + +func DecimalDecoder(ec bsoncodec.DecodeContext, vw bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != decimalType { + return bsoncodec.ValueEncoderError{Name: "DecimalDecoder", Kinds: []reflect.Kind{reflect.Struct}, Received: val} + } + str, err := vw.ReadString() + if err != nil { + return err + } + dBig, ok := new(decimal.Big).SetString(str) + if !ok { + return fmt.Errorf("cannot set string: <%s> to decimal.Big", str) + } + val.Set(reflect.ValueOf(utils.Decimal{dBig})) + return nil +} + // NewMongoStorage givese new mongo driver func NewMongoStorage(host, port, db, user, pass, mrshlerStr, storageType string, cdrsIndexes []string, ttl time.Duration) (ms *MongoStorage, err error) { @@ -140,10 +170,13 @@ func NewMongoStorage(host, port, db, user, pass, mrshlerStr, storageType string, } ctx := context.Background() url = "mongodb://" + url - reg := bson.NewRegistryBuilder().RegisterDecoder(tTime, bsoncodec.ValueDecoderFunc(TimeDecodeValue1)).Build() + reg := bson.NewRegistryBuilder() + reg.RegisterDecoder(tTime, bsoncodec.ValueDecoderFunc(TimeDecodeValue1)) + reg.RegisterTypeEncoder(decimalType, bsoncodec.ValueEncoderFunc(DecimalEncoder)) + reg.RegisterTypeDecoder(decimalType, bsoncodec.ValueDecoderFunc(DecimalDecoder)) opt := options.Client(). ApplyURI(url). - SetRegistry(reg). + SetRegistry(reg.Build()). SetServerSelectionTimeout(ttl). SetRetryWrites(false) // set this option to false because as default it is on true diff --git a/engine/tpreader.go b/engine/tpreader.go index e327256ef..8bcaf136c 100644 --- a/engine/tpreader.go +++ b/engine/tpreader.go @@ -2628,7 +2628,6 @@ func (tpr *TpReader) ReloadCache(caching string, verbose bool, opts map[string]i utils.DispatcherHostIDs: dphIDs, utils.RateProfileIDs: ratePrfIDs, utils.ActionProfileIDs: actionPrfIDs, - utils.AccountProfileIDs: accountPrfIDs, }, } diff --git a/loaders/loader.go b/loaders/loader.go index 92a75dc2f..0b27d6f69 100644 --- a/loaders/loader.go +++ b/loaders/loader.go @@ -682,7 +682,6 @@ func (ldr *Loader) storeLoadedData(loaderType string, if err := ldr.dm.SetAccountProfile(acp, true); err != nil { return err } - cacheArgs[utils.AccountProfileIDs] = ids } } } @@ -1039,7 +1038,6 @@ func (ldr *Loader) removeLoadedData(loaderType string, lds map[string][]LoaderDa tntIDStruct.ID, utils.NonTransactional, true); err != nil { return err } - cacheArgs[utils.AccountProfileIDs] = ids } } } diff --git a/utils/accountprofile.go b/utils/accountprofile.go index c329993ca..de6fbef72 100644 --- a/utils/accountprofile.go +++ b/utils/accountprofile.go @@ -21,8 +21,6 @@ package utils import ( "sort" "time" - - "github.com/ericlagergren/decimal" ) // AccountProfile represents one Account on a Tenant @@ -54,9 +52,9 @@ type Balance struct { // CostIncrement enforces cost calculation to specific balance increments type CostIncrement struct { FilterIDs []string - Increment *decimal.Big - FixedFee *decimal.Big - RecurrentFee *decimal.Big + Increment *Decimal + FixedFee *Decimal + RecurrentFee *Decimal } // Clone returns a copy of the CostIncrement @@ -69,31 +67,13 @@ func (cI *CostIncrement) Clone() (cIcln *CostIncrement) { } } if cI.Increment != nil { - cIcln.Increment = new(decimal.Big).Copy(cI.Increment) + cIcln.Increment = new(Decimal).Copy(cI.Increment) } if cI.FixedFee != nil { - cIcln.FixedFee = new(decimal.Big).Copy(cI.FixedFee) + cIcln.FixedFee = new(Decimal).Copy(cI.FixedFee) } if cI.RecurrentFee != nil { - cIcln.RecurrentFee = new(decimal.Big).Copy(cI.RecurrentFee) - } - return -} - -//Clone return a copy of the CostAttributes -func (cA *CostAttributes) Clone() (cstAtr *CostAttributes) { - cstAtr = new(CostAttributes) - if cA.FilterIDs != nil { - cstAtr.FilterIDs = make([]string, len(cA.FilterIDs)) - for i, value := range cA.FilterIDs { - cstAtr.FilterIDs[i] = value - } - } - if cA.AttributeProfileIDs != nil { - cstAtr.AttributeProfileIDs = make([]string, len(cA.AttributeProfileIDs)) - for i, value := range cA.AttributeProfileIDs { - cstAtr.AttributeProfileIDs[i] = value - } + cIcln.RecurrentFee = new(Decimal).Copy(cI.RecurrentFee) } return } @@ -108,7 +88,7 @@ func (uF *UnitFactor) Clone() (untFct *UnitFactor) { } } if uF.Factor != nil { - untFct.Factor = new(decimal.Big).Copy(uF.Factor) + untFct.Factor = new(Decimal).Copy(uF.Factor) } return } @@ -116,7 +96,7 @@ func (uF *UnitFactor) Clone() (untFct *UnitFactor) { // UnitFactor is a multiplicator for the usage received type UnitFactor struct { FilterIDs []string - Factor *decimal.Big + Factor *Decimal } func (aP *AccountProfile) TenantID() string { @@ -190,9 +170,9 @@ func (bL *Balance) Clone() (blnc *Balance) { } } if bL.CostAttributes != nil { - blnc.CostAttributes = make([]*CostAttributes, len(bL.CostAttributes)) + blnc.CostAttributes = make([]string, len(bL.CostAttributes)) for i, value := range bL.CostAttributes { - blnc.CostAttributes[i] = value.Clone() + blnc.CostAttributes[i] = value } } if bL.UnitFactors != nil { diff --git a/utils/accountprofile_test.go b/utils/accountprofile_test.go index eaeb49bbe..7297f3b14 100644 --- a/utils/accountprofile_test.go +++ b/utils/accountprofile_test.go @@ -39,21 +39,16 @@ func TestCloneBalance(t *testing.T) { CostIncrements: []*CostIncrement{ { FilterIDs: []string{"*string:~*req.Account:1001"}, - Increment: decimal.New(1, 1), - FixedFee: decimal.New(75, 1), - RecurrentFee: decimal.New(20, 1), - }, - }, - CostAttributes: []*CostAttributes{ - { - FilterIDs: []string{"*string:~*req.Account:1001"}, - AttributeProfileIDs: []string{"ID1"}, + Increment: &Decimal{decimal.New(1, 1)}, + FixedFee: &Decimal{decimal.New(75, 1)}, + RecurrentFee: &Decimal{decimal.New(20, 1)}, }, }, + CostAttributes: []string{"attr1", "attr2"}, UnitFactors: []*UnitFactor{ { FilterIDs: []string{"*string:~*req.Account:1001"}, - Factor: decimal.New(20, 2), + Factor: &Decimal{decimal.New(20, 2)}, }, }, Value: 1.25, @@ -89,21 +84,16 @@ func TestCloneAccountProfile(t *testing.T) { CostIncrements: []*CostIncrement{ { FilterIDs: []string{"*string:~*req.Account:1001"}, - Increment: decimal.New(1, 1), - FixedFee: decimal.New(75, 1), - RecurrentFee: decimal.New(20, 1), - }, - }, - CostAttributes: []*CostAttributes{ - { - FilterIDs: []string{"*string:~*req.Account:1001"}, - AttributeProfileIDs: []string{"ID1"}, + Increment: &Decimal{decimal.New(1, 1)}, + FixedFee: &Decimal{decimal.New(75, 1)}, + RecurrentFee: &Decimal{decimal.New(20, 1)}, }, }, + CostAttributes: []string{"attr1", "attr2"}, UnitFactors: []*UnitFactor{ { FilterIDs: []string{"*string:~*req.Account:1001"}, - Factor: decimal.New(20, 2), + Factor: &Decimal{decimal.New(20, 2)}, }, }, Value: 1.25, diff --git a/utils/consts.go b/utils/consts.go index 25fc1a2f5..9fd8191ea 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -196,7 +196,6 @@ var ( DispatcherHostIDs: DispatcherHostPrefix, RateProfileIDs: RateProfilePrefix, ActionProfileIDs: ActionProfilePrefix, - AccountProfileIDs: AccountProfilePrefix, TimingIDs: TimingsPrefix, AttributeFilterIndexIDs: AttributeFilterIndexes, @@ -237,7 +236,6 @@ var ( DispatcherHostIDs: CacheDispatcherHosts, RateProfileIDs: CacheRateProfiles, ActionProfileIDs: CacheActionProfiles, - AccountProfileIDs: CacheAccountProfiles, TimingIDs: CacheTimings, AttributeFilterIndexIDs: CacheAttributeFilterIndexes, @@ -1470,9 +1468,11 @@ const ( APIerSv1SetTPRateProfile = "APIerSv1.SetTPRateProfile" APIerSv1GetTPRateProfileIds = "APIerSv1.GetTPRateProfileIds" APIerSv1RemoveTPRateProfile = "APIerSv1.RemoveTPRateProfile" - APIerSv1GetTPAccountProfile = "APIerSv1.GetTPAccountProfile" - APIerSv1SetTPAccountProfile = "APIerSv1.SetTPAccountProfile" - APIerSv1RemoveTPAccountProfile = "APIerSv1.RemoveTPAccountProfile" + APIerSv1SetAccountProfile = "APIerSv1.SetAccountProfile" + APIerSv1GetAccountProfile = "APIerSv1.GetAccountProfile" + APIerSv1GetAccountProfileIDs = "APIerSv1.GetAccountProfileIDs" + APIerSv1RemoveAccountProfile = "APIerSv1.RemoveAccountProfile" + APIerSv1GetAccountProfileIDsCount = "APIerSv1.GetAccountProfileIDsCount" APIerSv1GetTPAccountProfileIDs = "APIerSv1.GetTPAccountProfileIDs" ) @@ -2597,7 +2597,6 @@ const ( RateProfileIDs = "RateProfileIDs" ActionProfileIDs = "ActionProfileIDs" TimingIDs = "TimingIDs" - AccountProfileIDs = "AccountProfileIDs" AttributeFilterIndexIDs = "AttributeFilterIndexIDs" ResourceFilterIndexIDs = "ResourceFilterIndexIDs" StatFilterIndexIDs = "StatFilterIndexIDs" diff --git a/utils/decimal.go b/utils/decimal.go index 7e8026ef0..3afe33081 100644 --- a/utils/decimal.go +++ b/utils/decimal.go @@ -18,7 +18,13 @@ along with this program. If not, see package utils -import "github.com/ericlagergren/decimal" +import ( + "bytes" + "fmt" + "strconv" + + "github.com/ericlagergren/decimal" +) func DivideBig(x, y *decimal.Big) *decimal.Big { return new(decimal.Big).Quo(x, y) @@ -35,3 +41,47 @@ func AddBig(x, y *decimal.Big) *decimal.Big { func SubstractBig(x, y *decimal.Big) *decimal.Big { return new(decimal.Big).Sub(x, y) } + +func NewDecimalFromFloat64(f float64) (*Decimal, error) { + d, canSet := new(decimal.Big).SetString(strconv.FormatFloat(f, 'f', -1, 64)) + if !canSet { + return nil, fmt.Errorf("cannot convert float64 to decimal.Big") + } + return &Decimal{d}, nil +} + +type Decimal struct { + *decimal.Big +} + +func (d *Decimal) UnmarshalBinary(data []byte) (err error) { + if d == nil { + d = &Decimal{new(decimal.Big)} + } + if d.Big == nil { + d.Big = new(decimal.Big) + } + return d.Big.UnmarshalText(data) +} + +func (d *Decimal) MarshalBinary() ([]byte, error) { + if d.Big == nil { + d.Big = new(decimal.Big) + } + return d.Big.MarshalText() +} + +func (d *Decimal) UnmarshalJSON(data []byte) (err error) { + return d.UnmarshalBinary(data) +} + +func (d *Decimal) MarshalJSON() ([]byte, error) { + x, err := d.MarshalText() + return bytes.Trim(x, `"`), err +} + +func (d *Decimal) Copy(d2 *Decimal) *Decimal { + d.Big = new(decimal.Big) + d.Big.Copy(d2.Big) + return d +}