diff --git a/apier/v1/tpusers.go b/apier/v1/tpusers.go
index 1049c38c1..e511f3c04 100644
--- a/apier/v1/tpusers.go
+++ b/apier/v1/tpusers.go
@@ -24,7 +24,7 @@ import (
// Creates a new alias within a tariff plan
func (self *ApierV1) SetTPUser(attrs utils.TPUsers, reply *string) error {
- if missing := utils.MissingStructFields(&attrs, []string{"TPid", "Direction", "Tenant", "Category", "Account", "Subject", "Group"}); len(missing) != 0 {
+ if missing := utils.MissingStructFields(&attrs, []string{"TPid", "UserName", "Tenant"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
if err := self.StorDb.SetTPUsers([]*utils.TPUsers{&attrs}); err != nil {
@@ -35,21 +35,22 @@ func (self *ApierV1) SetTPUser(attrs utils.TPUsers, reply *string) error {
}
type AttrGetTPUser struct {
- TPid string // Tariff plan id
- UserId string
+ TPid string // Tariff plan id
+ Tenant string
+ UserName string
}
// Queries specific User on Tariff plan
func (self *ApierV1) GetTPUser(attr AttrGetTPUser, reply *utils.TPUsers) error {
- if missing := utils.MissingStructFields(&attr, []string{"TPid"}); len(missing) != 0 { //Params missing
+ if missing := utils.MissingStructFields(&attr, []string{"TPid", "UserName", "Tenant"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
- filter := &utils.TPUsers{TPid: attr.TPid}
- filter.SetId(attr.UserId)
+ filter := &utils.TPUsers{TPid: attr.TPid, UserName: attr.UserName, Tenant: attr.Tenant}
if tms, err := self.StorDb.GetTPUsers(filter); err != nil {
- return utils.NewErrServerError(err)
- } else if len(tms) == 0 {
- return utils.ErrNotFound
+ if err.Error() != utils.ErrNotFound.Error() {
+ err = utils.NewErrServerError(err)
+ }
+ return err
} else {
*reply = *tms[0]
}
@@ -78,11 +79,14 @@ func (self *ApierV1) GetTPUserIds(attrs AttrGetTPUserIds, reply *[]string) error
// Removes specific User on Tariff plan
func (self *ApierV1) RemTPUser(attrs AttrGetTPUser, reply *string) error {
- if missing := utils.MissingStructFields(&attrs, []string{"TPid", "UserId"}); len(missing) != 0 { //Params missing
+ if missing := utils.MissingStructFields(&attrs, []string{"TPid", "Tenant", "UserName"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
- if err := self.StorDb.RemTpData(utils.TBLTPUsers, attrs.TPid, map[string]string{"tag": attrs.UserId}); err != nil {
- return utils.NewErrServerError(err)
+ if err := self.StorDb.RemTpData(utils.TBLTPUsers, attrs.TPid, map[string]string{"tenant": attrs.Tenant, "user_name": attrs.UserName}); err != nil {
+ if err.Error() != utils.ErrNotFound.Error() {
+ err = utils.NewErrServerError(err)
+ }
+ return err
} else {
*reply = utils.OK
}
diff --git a/apier/v1/tpusers_it_test.go b/apier/v1/tpusers_it_test.go
new file mode 100644
index 000000000..9f1c978bb
--- /dev/null
+++ b/apier/v1/tpusers_it_test.go
@@ -0,0 +1,241 @@
+// +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 (
+ "github.com/cgrates/cgrates/config"
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+ "net/rpc"
+ "net/rpc/jsonrpc"
+ "path"
+ "reflect"
+ "testing"
+)
+
+var (
+ tpUserCfgPath string
+ tpUserCfg *config.CGRConfig
+ tpUserRPC *rpc.Client
+ tpUserDataDir = "/usr/share/cgrates"
+ tpUser *utils.TPUsers
+ tpUserDelay int
+ tpUserConfigDIR string //run tests for specific configuration
+)
+
+var sTestsTPUsers = []func(t *testing.T){
+ testTPUsersInitCfg,
+ testTPUsersResetStorDb,
+ testTPUsersStartEngine,
+ testTPUsersRpcConn,
+ testTPUsersGetTPUserBeforeSet,
+ testTPUsersSetTPUser,
+ testTPUsersGetTPUserAfterSet,
+ testTPUsersGetTPUserID,
+ testTPUsersUpdateTPUser,
+ testTPUsersGetTPUserAfterUpdate,
+ testTPUsersRemTPUser,
+ testTPUsersGetTPUserAfterRemove,
+ testTPUsersKillEngine,
+}
+
+//Test start here
+func TestTPUserITMySql(t *testing.T) {
+ tpUserConfigDIR = "tutmysql"
+ for _, stest := range sTestsTPUsers {
+ t.Run(tpUserConfigDIR, stest)
+ }
+}
+
+func TestTPUserITMongo(t *testing.T) {
+ tpUserConfigDIR = "tutmongo"
+ for _, stest := range sTestsTPUsers {
+ t.Run(tpUserConfigDIR, stest)
+ }
+}
+
+func TestTPUserITPG(t *testing.T) {
+ tpUserConfigDIR = "tutpostgres"
+ for _, stest := range sTestsTPUsers {
+ t.Run(tpUserConfigDIR, stest)
+ }
+}
+
+func testTPUsersInitCfg(t *testing.T) {
+ var err error
+ tpUserCfgPath = path.Join(tpUserDataDir, "conf", "samples", tpUserConfigDIR)
+ tpUserCfg, err = config.NewCGRConfigFromFolder(tpUserCfgPath)
+ if err != nil {
+ t.Error(err)
+ }
+ tpUserCfg.DataFolderPath = tpUserDataDir // Share DataFolderPath through config towards StoreDb for Flush()
+ config.SetCgrConfig(tpUserCfg)
+ switch tpUserConfigDIR {
+ case "tutmongo": // Mongo needs more time to reset db
+ tpUserDelay = 4000
+ default:
+ tpUserDelay = 2000
+ }
+}
+
+// Wipe out the cdr database
+func testTPUsersResetStorDb(t *testing.T) {
+ if err := engine.InitStorDb(tpUserCfg); err != nil {
+ t.Fatal(err)
+ }
+}
+
+// Start CGR Engine
+func testTPUsersStartEngine(t *testing.T) {
+ if _, err := engine.StopStartEngine(tpUserCfgPath, tpUserDelay); err != nil {
+ t.Fatal(err)
+ }
+}
+
+// Connect rpc client to rater
+func testTPUsersRpcConn(t *testing.T) {
+ var err error
+ tpUserRPC, err = jsonrpc.Dial("tcp", tpUserCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func testTPUsersGetTPUserBeforeSet(t *testing.T) {
+ var reply *utils.TPUsers
+ if err := tpUserRPC.Call("ApierV1.GetTPUser", AttrGetTPUser{TPid: "TPU1", Tenant: "Tentant1", UserName: "User1"}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() {
+ t.Error(err)
+ }
+}
+
+func testTPUsersSetTPUser(t *testing.T) {
+ tpUser = &utils.TPUsers{
+ TPid: "TPU1",
+ UserName: "User1",
+ Tenant: "Tenant1",
+ Masked: true,
+ Weight: 20,
+ Profile: []*utils.TPUserProfile{
+ &utils.TPUserProfile{
+ AttrName: "UserProfile1",
+ AttrValue: "ValUP1",
+ },
+ &utils.TPUserProfile{
+ AttrName: "UserProfile2",
+ AttrValue: "ValUP2",
+ },
+ },
+ }
+ var result string
+ if err := tpUserRPC.Call("ApierV1.SetTPUser", tpUser, &result); err != nil {
+ t.Error(err)
+ } else if result != utils.OK {
+ t.Error("Unexpected reply returned", result)
+ }
+}
+
+func testTPUsersGetTPUserAfterSet(t *testing.T) {
+ var respond *utils.TPUsers
+ if err := tpUserRPC.Call("ApierV1.GetTPUser", &AttrGetTPUser{TPid: tpUser.TPid, UserName: tpUser.UserName, Tenant: tpUser.Tenant}, &respond); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(tpUser.TPid, respond.TPid) {
+ t.Errorf("Expecting: %+v, received: %+v", tpUser.TPid, respond.TPid)
+ } else if !reflect.DeepEqual(tpUser.UserName, respond.UserName) {
+ t.Errorf("Expecting: %+v, received: %+v", tpUser.UserName, respond.UserName)
+ } else if !reflect.DeepEqual(tpUser.Tenant, respond.Tenant) {
+ t.Errorf("Expecting: %+v, received: %+v", tpUser.Tenant, respond.Tenant)
+ } else if !reflect.DeepEqual(tpUser.Weight, respond.Weight) {
+ t.Errorf("Expecting: %+v, received: %+v", tpUser.Weight, respond.Weight)
+ } else if !reflect.DeepEqual(len(tpUser.Profile), len(respond.Profile)) {
+ t.Errorf("Expecting: %+v, received: %+v", len(tpUser.Profile), len(respond.Profile))
+ }
+}
+
+func testTPUsersGetTPUserID(t *testing.T) {
+ var result []string
+ expectedTPID := []string{"Tenant1:User1"}
+ if err := tpUserRPC.Call("ApierV1.GetTPUserIds", AttrGetTPUserIds{tpUser.TPid, utils.Paginator{}}, &result); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(result, expectedTPID) {
+ t.Errorf("Expecting: %+v, received: %+v", result, expectedTPID)
+ }
+}
+
+func testTPUsersUpdateTPUser(t *testing.T) {
+ var result string
+ tpUser.Profile = []*utils.TPUserProfile{
+ &utils.TPUserProfile{
+ AttrName: "UserProfile1",
+ AttrValue: "ValUp1",
+ },
+ &utils.TPUserProfile{
+ AttrName: "UserProfile2",
+ AttrValue: "ValUP2",
+ },
+ &utils.TPUserProfile{
+ AttrName: "UserProfile3",
+ AttrValue: "ValUP3",
+ },
+ }
+ if err := tpUserRPC.Call("ApierV1.SetTPUser", tpUser, &result); err != nil {
+ t.Error(err)
+ } else if result != utils.OK {
+ t.Error("Unexpected reply returned", result)
+ }
+}
+
+func testTPUsersGetTPUserAfterUpdate(t *testing.T) {
+ var respond *utils.TPUsers
+ if err := tpUserRPC.Call("ApierV1.GetTPUser", &AttrGetTPUser{TPid: tpUser.TPid, Tenant: tpUser.Tenant, UserName: tpUser.UserName}, &respond); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(tpUser.TPid, respond.TPid) {
+ t.Errorf("Expecting: %+v, received: %+v", tpUser.TPid, respond.TPid)
+ } else if !reflect.DeepEqual(tpUser.UserName, respond.UserName) {
+ t.Errorf("Expecting: %+v, received: %+v", tpUser.UserName, respond.UserName)
+ } else if !reflect.DeepEqual(tpUser.Tenant, respond.Tenant) {
+ t.Errorf("Expecting: %+v, received: %+v", tpUser.Tenant, respond.Tenant)
+ } else if !reflect.DeepEqual(tpUser.Weight, respond.Weight) {
+ t.Errorf("Expecting: %+v, received: %+v", tpUser.Weight, respond.Weight)
+ } else if !reflect.DeepEqual(len(tpUser.Profile), len(respond.Profile)) {
+ t.Errorf("Expecting: %+v, received: %+v", len(tpUser.Profile), len(respond.Profile))
+ }
+}
+
+func testTPUsersRemTPUser(t *testing.T) {
+ var resp string
+ if err := tpUserRPC.Call("ApierV1.RemTPUser", &AttrGetTPUser{TPid: tpUser.TPid, Tenant: tpUser.Tenant, UserName: tpUser.UserName}, &resp); err != nil {
+ t.Error(err)
+ } else if resp != utils.OK {
+ t.Error("Unexpected reply returned", resp)
+ }
+}
+
+func testTPUsersGetTPUserAfterRemove(t *testing.T) {
+ var respond *utils.TPUsers
+ if err := tpUserRPC.Call("ApierV1.GetTPUser", &AttrGetTPUser{TPid: "TPU1", UserName: "User1", Tenant: "Tenant1"}, &respond); err == nil || err.Error() != utils.ErrNotFound.Error() {
+ t.Error(err)
+ }
+}
+
+func testTPUsersKillEngine(t *testing.T) {
+ if err := engine.KillEngine(tpUserDelay); err != nil {
+ t.Error(err)
+ }
+}
diff --git a/data/conf/samples/tutmongo/cgrates.json b/data/conf/samples/tutmongo/cgrates.json
index 5bb324d5c..dd8d774d6 100644
--- a/data/conf/samples/tutmongo/cgrates.json
+++ b/data/conf/samples/tutmongo/cgrates.json
@@ -84,7 +84,7 @@
"stats": {
- "enabled": true,
+ "enabled": false,
"store_interval": "0s",
},
diff --git a/data/conf/samples/tutmysql/cgrates.json b/data/conf/samples/tutmysql/cgrates.json
index db6a53cb1..bb2983bd3 100644
--- a/data/conf/samples/tutmysql/cgrates.json
+++ b/data/conf/samples/tutmysql/cgrates.json
@@ -112,13 +112,13 @@
"stats": {
- "enabled": true,
+ "enabled": false,
"store_interval": "0s",
},
"historys": {
- "enabled": true,
+ "enabled": false,
},
diff --git a/data/conf/samples/tutpostgres/cgrates.json b/data/conf/samples/tutpostgres/cgrates.json
index 77b78db03..6ebb5d62b 100644
--- a/data/conf/samples/tutpostgres/cgrates.json
+++ b/data/conf/samples/tutpostgres/cgrates.json
@@ -60,8 +60,8 @@
"users": {
- "enabled": true, // starts User service: .
- "indexes": ["Uuid"], // user profile field indexes
+ "enabled": true,
+ "indexes": ["Uuid"],
},
@@ -80,4 +80,4 @@
},
-}
\ No newline at end of file
+}
diff --git a/engine/storage_mongo_stordb.go b/engine/storage_mongo_stordb.go
index 2915ee710..da90e63b8 100755
--- a/engine/storage_mongo_stordb.go
+++ b/engine/storage_mongo_stordb.go
@@ -20,13 +20,12 @@ package engine
import (
"fmt"
- "regexp"
- "strings"
- "time"
-
"github.com/cgrates/cgrates/utils"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
+ "regexp"
+ "strings"
+ "time"
)
func (ms *MongoStorage) GetTpIds() ([]string, error) {
@@ -64,7 +63,11 @@ func (ms *MongoStorage) GetTpTableIds(tpid, table string, distinct utils.TPDisti
for k, v := range filter {
findMap[k] = v
}
-
+ for k, v := range distinct { //fix for MongoStorage on TPUsers
+ if v == "user_name" {
+ distinct[k] = "username"
+ }
+ }
if pag != nil && pag.SearchTerm != "" {
var searchItems []bson.M
for _, d := range distinct {
@@ -522,6 +525,13 @@ func (ms *MongoStorage) RemTpData(table, tpid string, args map[string]string) er
if args == nil {
args = make(map[string]string)
}
+ for arg, val := range args { //fix for Mongo TPUsers tables
+ if arg == "user_name" {
+ delete(args, arg)
+ args["username"] = val
+ }
+ }
+
if _, has := args["tag"]; has { // API uses tag to be compatible with SQL models, fix it here
args["id"] = args["tag"]
delete(args, "tag")
diff --git a/engine/storage_sql.go b/engine/storage_sql.go
index fd557685b..a7263cab0 100755
--- a/engine/storage_sql.go
+++ b/engine/storage_sql.go
@@ -200,7 +200,6 @@ func (self *SQLStorage) RemTpData(table, tpid string, args map[string]string) er
tx.Commit()
return nil
}
- utils.Logger.Debug(fmt.Sprintf("#Rem sterge %s", tpid))
// Remove from a single table
tx = tx.Table(table).Where("tpid = ?", tpid)
// Compose filters
diff --git a/engine/tp_reader.go b/engine/tp_reader.go
index c5dc5eed8..584b91938 100755
--- a/engine/tp_reader.go
+++ b/engine/tp_reader.go
@@ -1949,7 +1949,7 @@ func (tpr *TpReader) WriteToDatabase(flush, verbose, disable_reverse bool) (err
}
}
if verbose {
- log.Print("StatQueueProfile:")
+ log.Print("StatQueueProfiles:")
}
for _, tpST := range tpr.stats {
st, err := APItoStats(tpST, tpr.timezone)