diff --git a/cmd/cgr-console/cgr-console.go b/cmd/cgr-console/cgr-console.go index bf6cf60a6..cb3312c41 100644 --- a/cmd/cgr-console/cgr-console.go +++ b/cmd/cgr-console/cgr-console.go @@ -82,6 +82,8 @@ func executeCommand(command string) { switch param.(type) { case *console.StringWrapper: param = param.(*console.StringWrapper).Item + case *console.StringSliceWrapper: + param = param.(*console.StringSliceWrapper).Items } //log.Printf("Param: %+v", param) diff --git a/console/user_indexes.go b/console/user_indexes.go index 965196e25..547e67381 100644 --- a/console/user_indexes.go +++ b/console/user_indexes.go @@ -58,6 +58,6 @@ func (self *CmdUserShowIndexes) PostprocessRpcParams() error { } func (self *CmdUserShowIndexes) RpcResult() interface{} { - s := []string{} + s := map[string][]string{} return &s } diff --git a/engine/users.go b/engine/users.go index 3ea20805a..9aa515302 100644 --- a/engine/users.go +++ b/engine/users.go @@ -1,6 +1,7 @@ package engine import ( + "log" "strings" "github.com/cgrates/cgrates/utils" @@ -33,13 +34,14 @@ type UserService interface { UpdateUser(UserProfile, *string) error GetUsers(UserProfile, *[]*UserProfile) error AddIndex([]string, *string) error - GetIndexes(string, *[]string) error + GetIndexes(string, *map[string][]string) error } type UserMap struct { - table map[string]map[string]string - index map[string][]string - ratingDb RatingStorage + table map[string]map[string]string + index map[string]map[string]bool + indexKeys []string + ratingDb RatingStorage } func NewUserMap(ratingDb RatingStorage) (*UserMap, error) { @@ -58,27 +60,29 @@ func NewUserMap(ratingDb RatingStorage) (*UserMap, error) { func newUserMap(ratingDb RatingStorage) *UserMap { return &UserMap{ table: make(map[string]map[string]string), - index: make(map[string][]string), + index: make(map[string]map[string]bool), ratingDb: ratingDb, } } func (um *UserMap) SetUser(up UserProfile, reply *string) error { - um.table[up.GetId()] = up.Profile if err := um.ratingDb.SetUser(&up); err != nil { *reply = err.Error() return err } + um.table[up.GetId()] = up.Profile + um.addIndex(&up) *reply = utils.OK return nil } func (um *UserMap) RemoveUser(up UserProfile, reply *string) error { - delete(um.table, up.GetId()) if err := um.ratingDb.RemoveUser(up.GetId()); err != nil { *reply = err.Error() return err } + delete(um.table, up.GetId()) + um.deleteIndex(&up) *reply = utils.OK return nil } @@ -90,20 +94,32 @@ func (um *UserMap) UpdateUser(up UserProfile, reply *string) error { return utils.ErrNotFound } if m == nil { - um.table[up.GetId()] = make(map[string]string, 0) + m = make(map[string]string) + } + oldM := make(map[string]string, len(m)) + for k, v := range m { + oldM[k] = v + } + oldUp := &UserProfile{ + Tenant: up.Tenant, + UserName: up.UserName, + Profile: oldM, } for key, value := range up.Profile { - um.table[up.GetId()][key] = value + m[key] = value } finalUp := &UserProfile{ Tenant: up.Tenant, UserName: up.UserName, - Profile: um.table[up.GetId()], + Profile: m, } if err := um.ratingDb.SetUser(finalUp); err != nil { *reply = err.Error() return err } + um.table[up.GetId()] = m + um.deleteIndex(oldUp) + um.addIndex(finalUp) *reply = utils.OK return nil } @@ -115,21 +131,21 @@ func (um *UserMap) GetUsers(up UserProfile, results *[]*UserProfile) error { // search index if up.Tenant != "" { if keys, found := um.index[utils.ConcatenatedKey("Tenant", up.Tenant)]; found { - for _, key := range keys { + for key := range keys { indexUnionKeys[key] = true } } } if up.UserName != "" { if keys, found := um.index[utils.ConcatenatedKey("UserName", up.UserName)]; found { - for _, key := range keys { + for key := range keys { indexUnionKeys[key] = true } } } for k, v := range up.Profile { if keys, found := um.index[utils.ConcatenatedKey(k, v)]; found { - for _, key := range keys { + for key := range keys { indexUnionKeys[key] = true } } @@ -172,38 +188,97 @@ func (um *UserMap) GetUsers(up UserProfile, results *[]*UserProfile) error { } func (um *UserMap) AddIndex(indexes []string, reply *string) error { + um.indexKeys = indexes for key, values := range um.table { - ud := &UserProfile{Profile: values} - ud.SetId(key) - for _, index := range indexes { - if index == "Tenant" { - if ud.Tenant != "" { - um.index[utils.ConcatenatedKey(index, ud.Tenant)] = append(um.index[utils.ConcatenatedKey(index, ud.Tenant)], key) - } - continue - } - if index == "UserName" { - if ud.UserName != "" { - um.index[utils.ConcatenatedKey(index, ud.UserName)] = append(um.index[utils.ConcatenatedKey(index, ud.UserName)], key) - } - continue - } - - for k, v := range ud.Profile { - if k == index && v != "" { - um.index[utils.ConcatenatedKey(k, v)] = append(um.index[utils.ConcatenatedKey(k, v)], key) - } - } - } + up := &UserProfile{Profile: values} + up.SetId(key) + um.addIndex(up) } *reply = utils.OK return nil } -func (um *UserMap) GetIndexes(in string, reply *[]string) error { - var indexes []string - for key := range um.index { - indexes = append(indexes, key) +func (um *UserMap) addIndex(up *UserProfile) { + key := up.GetId() + for _, index := range um.indexKeys { + if index == "Tenant" { + if up.Tenant != "" { + indexKey := utils.ConcatenatedKey(index, up.Tenant) + if um.index[indexKey] == nil { + um.index[indexKey] = make(map[string]bool) + } + um.index[indexKey][key] = true + } + continue + } + if index == "UserName" { + if up.UserName != "" { + indexKey := utils.ConcatenatedKey(index, up.UserName) + if um.index[indexKey] == nil { + um.index[indexKey] = make(map[string]bool) + } + um.index[indexKey][key] = true + } + continue + } + + for k, v := range up.Profile { + if k == index && v != "" { + indexKey := utils.ConcatenatedKey(k, v) + if um.index[indexKey] == nil { + um.index[indexKey] = make(map[string]bool) + } + um.index[indexKey][key] = true + } + } + } +} + +func (um *UserMap) deleteIndex(up *UserProfile) { + log.Printf("Delete index: %s: %v", up.GetId(), up.Profile) + key := up.GetId() + for _, index := range um.indexKeys { + if index == "Tenant" { + if up.Tenant != "" { + indexKey := utils.ConcatenatedKey(index, up.Tenant) + delete(um.index[indexKey], key) + if len(um.index[indexKey]) == 0 { + delete(um.index, indexKey) + } + } + continue + } + if index == "UserName" { + if up.UserName != "" { + indexKey := utils.ConcatenatedKey(index, up.UserName) + delete(um.index[indexKey], key) + if len(um.index[indexKey]) == 0 { + delete(um.index, indexKey) + } + } + continue + } + + for k, v := range up.Profile { + if k == index && v != "" { + indexKey := utils.ConcatenatedKey(k, v) + delete(um.index[indexKey], key) + if len(um.index[indexKey]) == 0 { + delete(um.index, indexKey) + } + } + } + } +} + +func (um *UserMap) GetIndexes(in string, reply *map[string][]string) error { + indexes := make(map[string][]string) + for key, values := range um.index { + var vs []string + for val := range values { + vs = append(vs, val) + } + indexes[key] = vs } *reply = indexes return nil @@ -243,6 +318,6 @@ func (ps *ProxyUserService) AddIndex(indexes []string, reply *string) error { return ps.Client.Call("UsersV1.AddIndex", indexes, reply) } -func (ps *ProxyUserService) GetIndexes(in string, reply *[]string) error { +func (ps *ProxyUserService) GetIndexes(in string, reply *map[string][]string) error { return ps.Client.Call("UsersV1.AddIndex", in, reply) } diff --git a/engine/users_test.go b/engine/users_test.go index 1820c6636..6184b9597 100644 --- a/engine/users_test.go +++ b/engine/users_test.go @@ -13,7 +13,7 @@ var testMap = UserMap{ "test:": map[string]string{"t": "v"}, "test1:user1": map[string]string{"t": "v", "x": "y"}, }, - index: make(map[string][]string), + index: make(map[string]map[string]bool), } func TestUsersAdd(t *testing.T) { @@ -267,7 +267,7 @@ func TestUsersAddIndex(t *testing.T) { func TestUsersAddIndexFull(t *testing.T) { var r string - testMap.index = make(map[string][]string) // reset index + testMap.index = make(map[string]map[string]bool) // reset index testMap.AddIndex([]string{"t", "x", "UserName", "Tenant"}, &r) if r != utils.OK || len(testMap.index) != 6 || @@ -278,7 +278,7 @@ func TestUsersAddIndexFull(t *testing.T) { func TestUsersAddIndexNone(t *testing.T) { var r string - testMap.index = make(map[string][]string) // reset index + testMap.index = make(map[string]map[string]bool) // reset index testMap.AddIndex([]string{"test"}, &r) if r != utils.OK || len(testMap.index) != 0 { @@ -288,7 +288,7 @@ func TestUsersAddIndexNone(t *testing.T) { func TestUsersGetFullindex(t *testing.T) { var r string - testMap.index = make(map[string][]string) // reset index + testMap.index = make(map[string]map[string]bool) // reset index testMap.AddIndex([]string{"t", "x", "UserName", "Tenant"}, &r) up := UserProfile{ Tenant: "test", @@ -306,7 +306,7 @@ func TestUsersGetFullindex(t *testing.T) { func TestUsersGetTenantindex(t *testing.T) { var r string - testMap.index = make(map[string][]string) // reset index + testMap.index = make(map[string]map[string]bool) // reset index testMap.AddIndex([]string{"t", "x", "UserName", "Tenant"}, &r) up := UserProfile{ Tenant: "testX", @@ -324,7 +324,7 @@ func TestUsersGetTenantindex(t *testing.T) { func TestUsersGetUserNameindex(t *testing.T) { var r string - testMap.index = make(map[string][]string) // reset index + testMap.index = make(map[string]map[string]bool) // reset index testMap.AddIndex([]string{"t", "x", "UserName", "Tenant"}, &r) up := UserProfile{ Tenant: "test", @@ -342,7 +342,7 @@ func TestUsersGetUserNameindex(t *testing.T) { func TestUsersGetNotFoundProfileindex(t *testing.T) { var r string - testMap.index = make(map[string][]string) // reset index + testMap.index = make(map[string]map[string]bool) // reset index testMap.AddIndex([]string{"t", "x", "UserName", "Tenant"}, &r) up := UserProfile{ Tenant: "test", @@ -360,7 +360,7 @@ func TestUsersGetNotFoundProfileindex(t *testing.T) { func TestUsersGetMissingTenantindex(t *testing.T) { var r string - testMap.index = make(map[string][]string) // reset index + testMap.index = make(map[string]map[string]bool) // reset index testMap.AddIndex([]string{"t", "x", "UserName", "Tenant"}, &r) up := UserProfile{ UserName: "user", @@ -377,7 +377,7 @@ func TestUsersGetMissingTenantindex(t *testing.T) { func TestUsersGetMissingUserNameindex(t *testing.T) { var r string - testMap.index = make(map[string][]string) // reset index + testMap.index = make(map[string]map[string]bool) // reset index testMap.AddIndex([]string{"t", "x", "UserName", "Tenant"}, &r) up := UserProfile{ Tenant: "test", @@ -394,7 +394,7 @@ func TestUsersGetMissingUserNameindex(t *testing.T) { func TestUsersGetMissingIdindex(t *testing.T) { var r string - testMap.index = make(map[string][]string) // reset index + testMap.index = make(map[string]map[string]bool) // reset index testMap.AddIndex([]string{"t", "x", "UserName", "Tenant"}, &r) up := UserProfile{ Profile: map[string]string{ @@ -410,7 +410,7 @@ func TestUsersGetMissingIdindex(t *testing.T) { func TestUsersGetMissingIdTwoINdex(t *testing.T) { var r string - testMap.index = make(map[string][]string) // reset index + testMap.index = make(map[string]map[string]bool) // reset index testMap.AddIndex([]string{"t", "x", "UserName", "Tenant"}, &r) up := UserProfile{ Profile: map[string]string{ diff --git a/utils/slice.go b/utils/slice.go index c8788f731..8a1014ee1 100644 --- a/utils/slice.go +++ b/utils/slice.go @@ -32,6 +32,14 @@ func IsSliceMember(ss []string, s string) bool { return false } +func SliceWithoutMember(ss []string, s string) []string { + sort.Strings(ss) + if i := sort.SearchStrings(ss, s); i < len(ss) && ss[i] == s { + ss[i], ss = ss[len(ss)-1], ss[:len(ss)-1] + } + return ss +} + //Iterates over slice members and returns true if one starts with prefix func SliceMemberHasPrefix(ss []string, prfx string) bool { for _, mbr := range ss {