diff options
Diffstat (limited to 'store')
-rw-r--r-- | store/layered_store.go | 34 | ||||
-rw-r--r-- | store/layered_store_supplier.go | 6 | ||||
-rw-r--r-- | store/local_cache_supplier.go | 8 | ||||
-rw-r--r-- | store/local_cache_supplier_roles.go | 68 | ||||
-rw-r--r-- | store/redis_supplier.go | 20 | ||||
-rw-r--r-- | store/sqlstore/role_store_test.go | 14 | ||||
-rw-r--r-- | store/sqlstore/role_supplier.go | 163 | ||||
-rw-r--r-- | store/sqlstore/store.go | 1 | ||||
-rw-r--r-- | store/sqlstore/supplier.go | 6 | ||||
-rw-r--r-- | store/store.go | 8 | ||||
-rw-r--r-- | store/storetest/mocks/LayeredStoreDatabaseLayer.go | 108 | ||||
-rw-r--r-- | store/storetest/mocks/LayeredStoreSupplier.go | 92 | ||||
-rw-r--r-- | store/storetest/mocks/RoleStore.go | 78 | ||||
-rw-r--r-- | store/storetest/mocks/SqlStore.go | 16 | ||||
-rw-r--r-- | store/storetest/mocks/Store.go | 16 | ||||
-rw-r--r-- | store/storetest/role_store.go | 244 | ||||
-rw-r--r-- | store/storetest/store.go | 3 |
17 files changed, 884 insertions, 1 deletions
diff --git a/store/layered_store.go b/store/layered_store.go index 65b4670c0..cac0f61d3 100644 --- a/store/layered_store.go +++ b/store/layered_store.go @@ -23,6 +23,7 @@ type LayeredStoreDatabaseLayer interface { type LayeredStore struct { TmpContext context.Context ReactionStore ReactionStore + RoleStore RoleStore DatabaseLayer LayeredStoreDatabaseLayer LocalCacheLayer *LocalCacheSupplier RedisLayer *RedisSupplier @@ -37,6 +38,7 @@ func NewLayeredStore(db LayeredStoreDatabaseLayer, metrics einterfaces.MetricsIn } store.ReactionStore = &LayeredReactionStore{store} + store.RoleStore = &LayeredRoleStore{store} // Setup the chain if ENABLE_EXPERIMENTAL_REDIS { @@ -161,6 +163,10 @@ func (s *LayeredStore) Plugin() PluginStore { return s.DatabaseLayer.Plugin() } +func (s *LayeredStore) Role() RoleStore { + return s.RoleStore +} + func (s *LayeredStore) MarkSystemRanUnitTests() { s.DatabaseLayer.MarkSystemRanUnitTests() } @@ -218,3 +224,31 @@ func (s *LayeredReactionStore) PermanentDeleteBatch(endTime int64, limit int64) return supplier.ReactionPermanentDeleteBatch(s.TmpContext, endTime, limit) }) } + +type LayeredRoleStore struct { + *LayeredStore +} + +func (s *LayeredRoleStore) Save(role *model.Role) StoreChannel { + return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult { + return supplier.RoleSave(s.TmpContext, role) + }) +} + +func (s *LayeredRoleStore) Get(roleId string) StoreChannel { + return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult { + return supplier.RoleGet(s.TmpContext, roleId) + }) +} + +func (s *LayeredRoleStore) GetByName(name string) StoreChannel { + return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult { + return supplier.RoleGetByName(s.TmpContext, name) + }) +} + +func (s *LayeredRoleStore) GetByNames(names []string) StoreChannel { + return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult { + return supplier.RoleGetByNames(s.TmpContext, names) + }) +} diff --git a/store/layered_store_supplier.go b/store/layered_store_supplier.go index 841b75a32..482ccd126 100644 --- a/store/layered_store_supplier.go +++ b/store/layered_store_supplier.go @@ -31,4 +31,10 @@ type LayeredStoreSupplier interface { ReactionGetForPost(ctx context.Context, postId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult ReactionDeleteAllWithEmojiName(ctx context.Context, emojiName string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult ReactionPermanentDeleteBatch(ctx context.Context, endTime int64, limit int64, hints ...LayeredStoreHint) *LayeredStoreSupplierResult + + // Roles + RoleSave(ctx context.Context, role *model.Role, hints ...LayeredStoreHint) *LayeredStoreSupplierResult + RoleGet(ctx context.Context, roleId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult + RoleGetByName(ctx context.Context, name string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult + RoleGetByNames(ctx context.Context, names []string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult } diff --git a/store/local_cache_supplier.go b/store/local_cache_supplier.go index 3627c5b39..2343f10a7 100644 --- a/store/local_cache_supplier.go +++ b/store/local_cache_supplier.go @@ -13,7 +13,10 @@ import ( const ( REACTION_CACHE_SIZE = 20000 - REACTION_CACHE_SEC = 1800 // 30 minutes + REACTION_CACHE_SEC = 30 * 60 + + ROLE_CACHE_SIZE = 20000 + ROLE_CACHE_SEC = 30 * 60 CLEAR_CACHE_MESSAGE_DATA = "" ) @@ -21,6 +24,7 @@ const ( type LocalCacheSupplier struct { next LayeredStoreSupplier reactionCache *utils.Cache + roleCache *utils.Cache metrics einterfaces.MetricsInterface cluster einterfaces.ClusterInterface } @@ -28,12 +32,14 @@ type LocalCacheSupplier struct { func NewLocalCacheSupplier(metrics einterfaces.MetricsInterface, cluster einterfaces.ClusterInterface) *LocalCacheSupplier { supplier := &LocalCacheSupplier{ reactionCache: utils.NewLruWithParams(REACTION_CACHE_SIZE, "Reaction", REACTION_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS), + roleCache: utils.NewLruWithParams(ROLE_CACHE_SIZE, "Role", ROLE_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES), metrics: metrics, cluster: cluster, } if cluster != nil { cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS, supplier.handleClusterInvalidateReaction) + cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES, supplier.handleClusterInvalidateRole) } return supplier diff --git a/store/local_cache_supplier_roles.go b/store/local_cache_supplier_roles.go new file mode 100644 index 000000000..a9cbda017 --- /dev/null +++ b/store/local_cache_supplier_roles.go @@ -0,0 +1,68 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package store + +import ( + "context" + + "github.com/mattermost/mattermost-server/model" +) + +func (s *LocalCacheSupplier) handleClusterInvalidateRole(msg *model.ClusterMessage) { + if msg.Data == CLEAR_CACHE_MESSAGE_DATA { + s.roleCache.Purge() + } else { + s.roleCache.Remove(msg.Data) + } +} + +func (s *LocalCacheSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + if len(role.Id) != 0 { + defer s.doInvalidateCacheCluster(s.roleCache, role.Name) + } + return s.Next().RoleSave(ctx, role, hints...) +} + +func (s *LocalCacheSupplier) RoleGet(ctx context.Context, roleId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + return s.Next().RoleGet(ctx, roleId, hints...) +} + +func (s *LocalCacheSupplier) RoleGetByName(ctx context.Context, name string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + if result := s.doStandardReadCache(ctx, s.roleCache, name, hints...); result != nil { + return result + } + + result := s.Next().RoleGetByName(ctx, name, hints...) + + s.doStandardAddToCache(ctx, s.roleCache, name, result, hints...) + + return result +} + +func (s *LocalCacheSupplier) RoleGetByNames(ctx context.Context, roleNames []string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + var foundRoles []*model.Role + var rolesToQuery []string + + for _, roleName := range roleNames { + if result := s.doStandardReadCache(ctx, s.roleCache, roleName, hints...); result != nil { + foundRoles = append(foundRoles, result.Data.(*model.Role)) + } else { + rolesToQuery = append(rolesToQuery, roleName) + } + } + + result := s.Next().RoleGetByNames(ctx, rolesToQuery, hints...) + + if result.Data != nil { + rolesFound := result.Data.([]*model.Role) + for _, role := range rolesFound { + res := NewSupplierResult() + res.Data = role + s.doStandardAddToCache(ctx, s.roleCache, role.Name, res, hints...) + } + result.Data = append(foundRoles, result.Data.([]*model.Role)...) + } + + return result +} diff --git a/store/redis_supplier.go b/store/redis_supplier.go index 32dc12033..b8ec794cf 100644 --- a/store/redis_supplier.go +++ b/store/redis_supplier.go @@ -132,3 +132,23 @@ func (s *RedisSupplier) ReactionPermanentDeleteBatch(ctx context.Context, endTim // Ignoring this. It's probably OK to have the emoji slowly expire from Redis. return s.Next().ReactionPermanentDeleteBatch(ctx, endTime, limit, hints...) } + +func (s *RedisSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + // TODO: Redis Caching. + return s.Next().RoleSave(ctx, role, hints...) +} + +func (s *RedisSupplier) RoleGet(ctx context.Context, roleId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + // TODO: Redis Caching. + return s.Next().RoleGet(ctx, roleId, hints...) +} + +func (s *RedisSupplier) RoleGetByName(ctx context.Context, name string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + // TODO: Redis Caching. + return s.Next().RoleGetByName(ctx, name, hints...) +} + +func (s *RedisSupplier) RoleGetByNames(ctx context.Context, roleNames []string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { + // TODO: Redis Caching. + return s.Next().RoleGetByNames(ctx, roleNames, hints...) +} diff --git a/store/sqlstore/role_store_test.go b/store/sqlstore/role_store_test.go new file mode 100644 index 000000000..e89930f71 --- /dev/null +++ b/store/sqlstore/role_store_test.go @@ -0,0 +1,14 @@ +// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package sqlstore + +import ( + "testing" + + "github.com/mattermost/mattermost-server/store/storetest" +) + +func TestRoleStore(t *testing.T) { + StoreTest(t, storetest.TestRoleStore) +} diff --git a/store/sqlstore/role_supplier.go b/store/sqlstore/role_supplier.go new file mode 100644 index 000000000..41eed85e0 --- /dev/null +++ b/store/sqlstore/role_supplier.go @@ -0,0 +1,163 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package sqlstore + +import ( + "context" + "database/sql" + "fmt" + "net/http" + "strings" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +type Role struct { + Id string + Name string + DisplayName string + Description string + Permissions string + SchemeManaged bool +} + +func NewRoleFromModel(role *model.Role) *Role { + permissionsMap := make(map[string]bool) + permissions := "" + + for _, permission := range role.Permissions { + if !permissionsMap[permission] { + permissions += fmt.Sprintf(" %v", permission) + permissionsMap[permission] = true + } + } + + return &Role{ + Id: role.Id, + Name: role.Name, + DisplayName: role.DisplayName, + Description: role.Description, + Permissions: permissions, + SchemeManaged: role.SchemeManaged, + } +} + +func (role Role) ToModel() *model.Role { + return &model.Role{ + Id: role.Id, + Name: role.Name, + DisplayName: role.DisplayName, + Description: role.Description, + Permissions: strings.Fields(role.Permissions), + SchemeManaged: role.SchemeManaged, + } +} + +func initSqlSupplierRoles(sqlStore SqlStore) { + for _, db := range sqlStore.GetAllConns() { + table := db.AddTableWithName(Role{}, "Roles").SetKeys(false, "Id") + table.ColMap("Name").SetMaxSize(64).SetUnique(true) + table.ColMap("DisplayName").SetMaxSize(128) + table.ColMap("Description").SetMaxSize(1024) + table.ColMap("Permissions").SetMaxSize(4096) + } +} + +func (s *SqlSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + result := store.NewSupplierResult() + + // Check the role is valid before proceeding. + if !role.IsValidWithoutId() { + result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.invalid_role.app_error", nil, "", http.StatusBadRequest) + return result + } + + dbRole := NewRoleFromModel(role) + if len(dbRole.Id) == 0 { + dbRole.Id = model.NewId() + if err := s.GetMaster().Insert(dbRole); err != nil { + result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.insert.app_error", nil, err.Error(), http.StatusInternalServerError) + } + } else { + if rowsChanged, err := s.GetMaster().Update(dbRole); err != nil { + result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.update.app_error", nil, err.Error(), http.StatusInternalServerError) + } else if rowsChanged != 1 { + result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.update.app_error", nil, "no record to update", http.StatusInternalServerError) + } + } + + result.Data = dbRole.ToModel() + + return result +} + +func (s *SqlSupplier) RoleGet(ctx context.Context, roleId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + result := store.NewSupplierResult() + + var dbRole Role + + if err := s.GetReplica().SelectOne(&dbRole, "SELECT * from Roles WHERE Id = :Id", map[string]interface{}{"Id": roleId}); err != nil { + if err == sql.ErrNoRows { + result.Err = model.NewAppError("SqlRoleStore.Get", "store.sql_role.get.app_error", nil, "Id="+roleId+", "+err.Error(), http.StatusNotFound) + } else { + result.Err = model.NewAppError("SqlRoleStore.Get", "store.sql_role.get.app_error", nil, err.Error(), http.StatusInternalServerError) + } + } + + result.Data = dbRole.ToModel() + + return result +} + +func (s *SqlSupplier) RoleGetByName(ctx context.Context, name string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + result := store.NewSupplierResult() + + var dbRole Role + + if err := s.GetReplica().SelectOne(&dbRole, "SELECT * from Roles WHERE Name = :Name", map[string]interface{}{"Name": name}); err != nil { + if err == sql.ErrNoRows { + result.Err = model.NewAppError("SqlRoleStore.GetByName", "store.sql_role.get_by_name.app_error", nil, "name="+name+",err="+err.Error(), http.StatusNotFound) + } else { + result.Err = model.NewAppError("SqlRoleStore.GetByName", "store.sql_role.get_by_name.app_error", nil, "name="+name+",err="+err.Error(), http.StatusInternalServerError) + } + } + + result.Data = dbRole.ToModel() + + return result +} + +func (s *SqlSupplier) RoleGetByNames(ctx context.Context, names []string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + result := store.NewSupplierResult() + + var dbRoles []*Role + + if len(names) == 0 { + result.Data = []*model.Role{} + return result + } + + var searchPlaceholders []string + var parameters = map[string]interface{}{} + for i, value := range names { + searchPlaceholders = append(searchPlaceholders, fmt.Sprintf(":Name%d", i)) + parameters[fmt.Sprintf("Name%d", i)] = value + } + + searchTerm := "Name IN (" + strings.Join(searchPlaceholders, ", ") + ")" + + if _, err := s.GetReplica().Select(&dbRoles, "SELECT * from Roles WHERE "+searchTerm, parameters); err != nil { + result.Err = model.NewAppError("SqlRoleStore.GetByNames", "store.sql_role.get_by_names.app_error", nil, err.Error(), http.StatusInternalServerError) + } + + var roles []*model.Role + for _, dbRole := range dbRoles { + roles = append(roles, dbRole.ToModel()) + } + + result.Data = roles + + return result +} diff --git a/store/sqlstore/store.go b/store/sqlstore/store.go index cfdd7a552..1c623f0b1 100644 --- a/store/sqlstore/store.go +++ b/store/sqlstore/store.go @@ -87,4 +87,5 @@ type SqlStore interface { Job() store.JobStore Plugin() store.PluginStore UserAccessToken() store.UserAccessTokenStore + Role() store.RoleStore } diff --git a/store/sqlstore/supplier.go b/store/sqlstore/supplier.go index 3b9528578..5e43ee0f0 100644 --- a/store/sqlstore/supplier.go +++ b/store/sqlstore/supplier.go @@ -86,6 +86,7 @@ type SqlSupplierOldStores struct { userAccessToken store.UserAccessTokenStore plugin store.PluginStore channelMemberHistory store.ChannelMemberHistoryStore + role store.RoleStore } type SqlSupplier struct { @@ -135,6 +136,7 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter supplier.oldStores.plugin = NewSqlPluginStore(supplier) initSqlSupplierReactions(supplier) + initSqlSupplierRoles(supplier) err := supplier.GetMaster().CreateTablesIfNotExists() if err != nil { @@ -811,6 +813,10 @@ func (ss *SqlSupplier) Plugin() store.PluginStore { return ss.oldStores.plugin } +func (ss *SqlSupplier) Role() store.RoleStore { + return ss.oldStores.role +} + func (ss *SqlSupplier) DropAllTables() { ss.master.TruncateTables() } diff --git a/store/store.go b/store/store.go index 85f215ab9..9364218c8 100644 --- a/store/store.go +++ b/store/store.go @@ -61,6 +61,7 @@ type Store interface { Status() StatusStore FileInfo() FileInfoStore Reaction() ReactionStore + Role() RoleStore Job() JobStore UserAccessToken() UserAccessTokenStore ChannelMemberHistory() ChannelMemberHistoryStore @@ -462,3 +463,10 @@ type PluginStore interface { Get(pluginId, key string) StoreChannel Delete(pluginId, key string) StoreChannel } + +type RoleStore interface { + Save(role *model.Role) StoreChannel + Get(roleId string) StoreChannel + GetByName(name string) StoreChannel + GetByNames(names []string) StoreChannel +} diff --git a/store/storetest/mocks/LayeredStoreDatabaseLayer.go b/store/storetest/mocks/LayeredStoreDatabaseLayer.go index 9c66c4aac..d0162a01e 100644 --- a/store/storetest/mocks/LayeredStoreDatabaseLayer.go +++ b/store/storetest/mocks/LayeredStoreDatabaseLayer.go @@ -416,6 +416,114 @@ func (_m *LayeredStoreDatabaseLayer) ReactionSave(ctx context.Context, reaction return r0 } +// Role provides a mock function with given fields: +func (_m *LayeredStoreDatabaseLayer) Role() store.RoleStore { + ret := _m.Called() + + var r0 store.RoleStore + if rf, ok := ret.Get(0).(func() store.RoleStore); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.RoleStore) + } + } + + return r0 +} + +// RoleGet provides a mock function with given fields: ctx, roleId, hints +func (_m *LayeredStoreDatabaseLayer) RoleGet(ctx context.Context, roleId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + _va := make([]interface{}, len(hints)) + for _i := range hints { + _va[_i] = hints[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, roleId) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, roleId, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + +// RoleGetByName provides a mock function with given fields: ctx, name, hints +func (_m *LayeredStoreDatabaseLayer) RoleGetByName(ctx context.Context, name string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + _va := make([]interface{}, len(hints)) + for _i := range hints { + _va[_i] = hints[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, name) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, name, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + +// RoleGetByNames provides a mock function with given fields: ctx, names, hints +func (_m *LayeredStoreDatabaseLayer) RoleGetByNames(ctx context.Context, names []string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + _va := make([]interface{}, len(hints)) + for _i := range hints { + _va[_i] = hints[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, names) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, []string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, names, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + +// RoleSave provides a mock function with given fields: ctx, role, hints +func (_m *LayeredStoreDatabaseLayer) RoleSave(ctx context.Context, role *model.Role, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + _va := make([]interface{}, len(hints)) + for _i := range hints { + _va[_i] = hints[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, role) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, *model.Role, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, role, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + // Session provides a mock function with given fields: func (_m *LayeredStoreDatabaseLayer) Session() store.SessionStore { ret := _m.Called() diff --git a/store/storetest/mocks/LayeredStoreSupplier.go b/store/storetest/mocks/LayeredStoreSupplier.go index f4187dae9..59fd31cb8 100644 --- a/store/storetest/mocks/LayeredStoreSupplier.go +++ b/store/storetest/mocks/LayeredStoreSupplier.go @@ -145,6 +145,98 @@ func (_m *LayeredStoreSupplier) ReactionSave(ctx context.Context, reaction *mode return r0 } +// RoleGet provides a mock function with given fields: ctx, roleId, hints +func (_m *LayeredStoreSupplier) RoleGet(ctx context.Context, roleId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + _va := make([]interface{}, len(hints)) + for _i := range hints { + _va[_i] = hints[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, roleId) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, roleId, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + +// RoleGetByName provides a mock function with given fields: ctx, name, hints +func (_m *LayeredStoreSupplier) RoleGetByName(ctx context.Context, name string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + _va := make([]interface{}, len(hints)) + for _i := range hints { + _va[_i] = hints[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, name) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, name, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + +// RoleGetByNames provides a mock function with given fields: ctx, names, hints +func (_m *LayeredStoreSupplier) RoleGetByNames(ctx context.Context, names []string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + _va := make([]interface{}, len(hints)) + for _i := range hints { + _va[_i] = hints[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, names) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, []string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, names, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + +// RoleSave provides a mock function with given fields: ctx, role, hints +func (_m *LayeredStoreSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { + _va := make([]interface{}, len(hints)) + for _i := range hints { + _va[_i] = hints[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, role) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *store.LayeredStoreSupplierResult + if rf, ok := ret.Get(0).(func(context.Context, *model.Role, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok { + r0 = rf(ctx, role, hints...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*store.LayeredStoreSupplierResult) + } + } + + return r0 +} + // SetChainNext provides a mock function with given fields: _a0 func (_m *LayeredStoreSupplier) SetChainNext(_a0 store.LayeredStoreSupplier) { _m.Called(_a0) diff --git a/store/storetest/mocks/RoleStore.go b/store/storetest/mocks/RoleStore.go new file mode 100644 index 000000000..8150460ae --- /dev/null +++ b/store/storetest/mocks/RoleStore.go @@ -0,0 +1,78 @@ +// Code generated by mockery v1.0.0 + +// Regenerate this file using `make store-mocks`. + +package mocks + +import mock "github.com/stretchr/testify/mock" +import model "github.com/mattermost/mattermost-server/model" +import store "github.com/mattermost/mattermost-server/store" + +// RoleStore is an autogenerated mock type for the RoleStore type +type RoleStore struct { + mock.Mock +} + +// Get provides a mock function with given fields: roleId +func (_m *RoleStore) Get(roleId string) store.StoreChannel { + ret := _m.Called(roleId) + + var r0 store.StoreChannel + if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok { + r0 = rf(roleId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.StoreChannel) + } + } + + return r0 +} + +// GetByName provides a mock function with given fields: name +func (_m *RoleStore) GetByName(name string) store.StoreChannel { + ret := _m.Called(name) + + var r0 store.StoreChannel + if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok { + r0 = rf(name) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.StoreChannel) + } + } + + return r0 +} + +// GetByNames provides a mock function with given fields: names +func (_m *RoleStore) GetByNames(names []string) store.StoreChannel { + ret := _m.Called(names) + + var r0 store.StoreChannel + if rf, ok := ret.Get(0).(func([]string) store.StoreChannel); ok { + r0 = rf(names) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.StoreChannel) + } + } + + return r0 +} + +// Save provides a mock function with given fields: role +func (_m *RoleStore) Save(role *model.Role) store.StoreChannel { + ret := _m.Called(role) + + var r0 store.StoreChannel + if rf, ok := ret.Get(0).(func(*model.Role) store.StoreChannel); ok { + r0 = rf(role) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.StoreChannel) + } + } + + return r0 +} diff --git a/store/storetest/mocks/SqlStore.go b/store/storetest/mocks/SqlStore.go index b9b962101..43709fc0e 100644 --- a/store/storetest/mocks/SqlStore.go +++ b/store/storetest/mocks/SqlStore.go @@ -538,6 +538,22 @@ func (_m *SqlStore) RenameColumnIfExists(tableName string, oldColumnName string, return r0 } +// Role provides a mock function with given fields: +func (_m *SqlStore) Role() store.RoleStore { + ret := _m.Called() + + var r0 store.RoleStore + if rf, ok := ret.Get(0).(func() store.RoleStore); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.RoleStore) + } + } + + return r0 +} + // Session provides a mock function with given fields: func (_m *SqlStore) Session() store.SessionStore { ret := _m.Called() diff --git a/store/storetest/mocks/Store.go b/store/storetest/mocks/Store.go index 40b50a554..cb7e511f6 100644 --- a/store/storetest/mocks/Store.go +++ b/store/storetest/mocks/Store.go @@ -283,6 +283,22 @@ func (_m *Store) Reaction() store.ReactionStore { return r0 } +// Role provides a mock function with given fields: +func (_m *Store) Role() store.RoleStore { + ret := _m.Called() + + var r0 store.RoleStore + if rf, ok := ret.Get(0).(func() store.RoleStore); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.RoleStore) + } + } + + return r0 +} + // Session provides a mock function with given fields: func (_m *Store) Session() store.SessionStore { ret := _m.Called() diff --git a/store/storetest/role_store.go b/store/storetest/role_store.go new file mode 100644 index 000000000..499e36e1e --- /dev/null +++ b/store/storetest/role_store.go @@ -0,0 +1,244 @@ +// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestRoleStore(t *testing.T, ss store.Store) { + t.Run("Save", func(t *testing.T) { testRoleStoreSave(t, ss) }) + t.Run("Get", func(t *testing.T) { testRoleStoreGet(t, ss) }) + t.Run("GetByName", func(t *testing.T) { testRoleStoreGetByName(t, ss) }) + t.Run("GetNames", func(t *testing.T) { testRoleStoreGetByNames(t, ss) }) +} + +func testRoleStoreSave(t *testing.T, ss store.Store) { + // Save a new role. + r1 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{ + "invite_user", + "create_public_channel", + "add_user_to_team", + }, + SchemeManaged: false, + } + + res1 := <-ss.Role().Save(r1) + assert.Nil(t, res1.Err) + d1 := res1.Data.(*model.Role) + assert.Len(t, d1.Id, 26) + assert.Equal(t, r1.Name, d1.Name) + assert.Equal(t, r1.DisplayName, d1.DisplayName) + assert.Equal(t, r1.Description, d1.Description) + assert.Equal(t, r1.Permissions, d1.Permissions) + assert.Equal(t, r1.SchemeManaged, d1.SchemeManaged) + + // Change the role permissions and update. + d1.Permissions = []string{ + "invite_user", + "add_user_to_team", + "delete_public_channel", + } + + res2 := <-ss.Role().Save(d1) + assert.Nil(t, res2.Err) + d2 := res2.Data.(*model.Role) + assert.Len(t, d2.Id, 26) + assert.Equal(t, r1.Name, d2.Name) + assert.Equal(t, r1.DisplayName, d2.DisplayName) + assert.Equal(t, r1.Description, d2.Description) + assert.Equal(t, d1.Permissions, d2.Permissions) + assert.Equal(t, r1.SchemeManaged, d2.SchemeManaged) + + // Try saving one with an invalid ID set. + r3 := &model.Role{ + Id: model.NewId(), + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{ + "invite_user", + "create_public_channel", + "add_user_to_team", + }, + SchemeManaged: false, + } + + res3 := <-ss.Role().Save(r3) + assert.NotNil(t, res3.Err) + + // Try saving one with a duplicate "name" field. + r4 := &model.Role{ + Name: r1.Name, + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{ + "invite_user", + "create_public_channel", + "add_user_to_team", + }, + SchemeManaged: false, + } + + res4 := <-ss.Role().Save(r4) + assert.NotNil(t, res4.Err) +} + +func testRoleStoreGet(t *testing.T, ss store.Store) { + // Save a role to test with. + r1 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{ + "invite_user", + "create_public_channel", + "add_user_to_team", + }, + SchemeManaged: false, + } + + res1 := <-ss.Role().Save(r1) + assert.Nil(t, res1.Err) + d1 := res1.Data.(*model.Role) + assert.Len(t, d1.Id, 26) + + // Get a valid role + res2 := <-ss.Role().Get(d1.Id) + assert.Nil(t, res2.Err) + d2 := res1.Data.(*model.Role) + assert.Equal(t, d1.Id, d2.Id) + assert.Equal(t, r1.Name, d2.Name) + assert.Equal(t, r1.DisplayName, d2.DisplayName) + assert.Equal(t, r1.Description, d2.Description) + assert.Equal(t, r1.Permissions, d2.Permissions) + assert.Equal(t, r1.SchemeManaged, d2.SchemeManaged) + + // Get an invalid role + res3 := <-ss.Role().Get(model.NewId()) + assert.NotNil(t, res3.Err) +} + +func testRoleStoreGetByName(t *testing.T, ss store.Store) { + // Save a role to test with. + r1 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{ + "invite_user", + "create_public_channel", + "add_user_to_team", + }, + SchemeManaged: false, + } + + res1 := <-ss.Role().Save(r1) + assert.Nil(t, res1.Err) + d1 := res1.Data.(*model.Role) + assert.Len(t, d1.Id, 26) + + // Get a valid role + res2 := <-ss.Role().GetByName(d1.Name) + assert.Nil(t, res2.Err) + d2 := res1.Data.(*model.Role) + assert.Equal(t, d1.Id, d2.Id) + assert.Equal(t, r1.Name, d2.Name) + assert.Equal(t, r1.DisplayName, d2.DisplayName) + assert.Equal(t, r1.Description, d2.Description) + assert.Equal(t, r1.Permissions, d2.Permissions) + assert.Equal(t, r1.SchemeManaged, d2.SchemeManaged) + + // Get an invalid role + res3 := <-ss.Role().GetByName(model.NewId()) + assert.NotNil(t, res3.Err) +} + +func testRoleStoreGetByNames(t *testing.T, ss store.Store) { + // Save some roles to test with. + r1 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{ + "invite_user", + "create_public_channel", + "add_user_to_team", + }, + SchemeManaged: false, + } + r2 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{ + "read_channel", + "create_public_channel", + "add_user_to_team", + }, + SchemeManaged: false, + } + r3 := &model.Role{ + Name: model.NewId(), + DisplayName: model.NewId(), + Description: model.NewId(), + Permissions: []string{ + "invite_user", + "delete_private_channel", + "add_user_to_team", + }, + SchemeManaged: false, + } + + res1 := <-ss.Role().Save(r1) + assert.Nil(t, res1.Err) + d1 := res1.Data.(*model.Role) + assert.Len(t, d1.Id, 26) + + res2 := <-ss.Role().Save(r2) + assert.Nil(t, res2.Err) + d2 := res2.Data.(*model.Role) + assert.Len(t, d2.Id, 26) + + res3 := <-ss.Role().Save(r3) + assert.Nil(t, res3.Err) + d3 := res3.Data.(*model.Role) + assert.Len(t, d3.Id, 26) + + // Get two valid roles. + n4 := []string{r1.Name, r2.Name} + res4 := <-ss.Role().GetByNames(n4) + assert.Nil(t, res4.Err) + roles4 := res4.Data.([]*model.Role) + assert.Len(t, roles4, 2) + assert.Contains(t, roles4, d1) + assert.Contains(t, roles4, d2) + assert.NotContains(t, roles4, d3) + + // Get two invalid roles. + n5 := []string{model.NewId(), model.NewId()} + res5 := <-ss.Role().GetByNames(n5) + assert.Nil(t, res5.Err) + roles5 := res5.Data.([]*model.Role) + assert.Len(t, roles5, 0) + + // Get one valid one and one invalid one. + n6 := []string{r1.Name, model.NewId()} + res6 := <-ss.Role().GetByNames(n6) + assert.Nil(t, res6.Err) + roles6 := res6.Data.([]*model.Role) + assert.Len(t, roles6, 1) + assert.Contains(t, roles6, d1) + assert.NotContains(t, roles6, d2) + assert.NotContains(t, roles6, d3) +} diff --git a/store/storetest/store.go b/store/storetest/store.go index 367c5f441..44f426075 100644 --- a/store/storetest/store.go +++ b/store/storetest/store.go @@ -43,6 +43,7 @@ type Store struct { UserAccessTokenStore mocks.UserAccessTokenStore PluginStore mocks.PluginStore ChannelMemberHistoryStore mocks.ChannelMemberHistoryStore + RoleStore mocks.RoleStore } func (s *Store) Team() store.TeamStore { return &s.TeamStore } @@ -68,6 +69,7 @@ func (s *Store) Reaction() store.ReactionStore { return &s.React func (s *Store) Job() store.JobStore { return &s.JobStore } func (s *Store) UserAccessToken() store.UserAccessTokenStore { return &s.UserAccessTokenStore } func (s *Store) Plugin() store.PluginStore { return &s.PluginStore } +func (s *Store) Role() store.RoleStore { return &s.RoleStore } func (s *Store) ChannelMemberHistory() store.ChannelMemberHistoryStore { return &s.ChannelMemberHistoryStore } @@ -104,5 +106,6 @@ func (s *Store) AssertExpectations(t mock.TestingT) bool { &s.UserAccessTokenStore, &s.ChannelMemberHistoryStore, &s.PluginStore, + &s.RoleStore, ) } |