MM-8796: Full implementation of "Schemes" in Store/Model/App layers. (#8357)

* Add Scheme model and stub store.

* Port ChannelStore to be Scheme aware.

* Make almost all the API/APP layer work with ChannelSchemes.

Only thing still hacky is UpdateChannelMemberRoles().

* Add basic SchemeStore implementation.

* Migrate UpdateChannelMemberRoles properly and fix tests.

* Update store tests and mocks so they work.

* Include creating default roles in Scheme create store function.

* Implement role deletion and start scheme deletion.

* Only use non-deleted roles for authorization.

* Add GetByScheme method to Team store.

* Add GetChannelsByScheme.

* Update store mocks.

* Implement scheme deletion in the store.

* Rename is valid function.

* Add offset and limit to queries to fetch teams and channels by scheme.

* Fix queries.

* Implement scheme awareness in Team store and add a migration.

* Tidy up ChannelStore mapping functions and add exhaustive unit tests.

* Add all missing i18n.

* Proper tests for TeamStore internal functions and fix them.

* Make additional TeamMember fields nullable.

* Make new ChannelMember fields nullable.

* Create new nullable columns without defaults.

* Make new fields in large tables nullalble.

* Fix empty list of TeamMembers.

* Deduplicate SQL queries.

* Fix spelling.

* Fix review comment.

* More review fixes.

* More review fixes.
This commit is contained in:
George Goldberg
2018-04-20 19:49:13 +01:00
committed by Martin Kraft
parent 853445dc2e
commit cd55c44c8f
48 changed files with 3605 additions and 176 deletions

View File

@@ -24,6 +24,7 @@ type LayeredStore struct {
TmpContext context.Context
ReactionStore ReactionStore
RoleStore RoleStore
SchemeStore SchemeStore
DatabaseLayer LayeredStoreDatabaseLayer
LocalCacheLayer *LocalCacheSupplier
RedisLayer *RedisSupplier
@@ -39,6 +40,7 @@ func NewLayeredStore(db LayeredStoreDatabaseLayer, metrics einterfaces.MetricsIn
store.ReactionStore = &LayeredReactionStore{store}
store.RoleStore = &LayeredRoleStore{store}
store.SchemeStore = &LayeredSchemeStore{store}
// Setup the chain
if ENABLE_EXPERIMENTAL_REDIS {
@@ -167,6 +169,10 @@ func (s *LayeredStore) Role() RoleStore {
return s.RoleStore
}
func (s *LayeredStore) Scheme() SchemeStore {
return s.SchemeStore
}
func (s *LayeredStore) MarkSystemRanUnitTests() {
s.DatabaseLayer.MarkSystemRanUnitTests()
}
@@ -253,8 +259,36 @@ func (s *LayeredRoleStore) GetByNames(names []string) StoreChannel {
})
}
func (s *LayeredRoleStore) Delete(roldId string) StoreChannel {
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
return supplier.RoleDelete(s.TmpContext, roldId)
})
}
func (s *LayeredRoleStore) PermanentDeleteAll() StoreChannel {
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
return supplier.RolePermanentDeleteAll(s.TmpContext)
})
}
type LayeredSchemeStore struct {
*LayeredStore
}
func (s *LayeredSchemeStore) Save(scheme *model.Scheme) StoreChannel {
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
return supplier.SchemeSave(s.TmpContext, scheme)
})
}
func (s *LayeredSchemeStore) Get(schemeId string) StoreChannel {
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
return supplier.SchemeGet(s.TmpContext, schemeId)
})
}
func (s *LayeredSchemeStore) Delete(schemeId string) StoreChannel {
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
return supplier.SchemeDelete(s.TmpContext, schemeId)
})
}

View File

@@ -35,5 +35,11 @@ type LayeredStoreSupplier interface {
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
RoleDelete(ctx context.Context, roldId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
RolePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
// Schemes
SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
SchemeGet(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
SchemeDelete(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
}

View File

@@ -18,6 +18,9 @@ const (
ROLE_CACHE_SIZE = 20000
ROLE_CACHE_SEC = 30 * 60
SCHEME_CACHE_SIZE = 20000
SCHEME_CACHE_SEC = 30 * 60
CLEAR_CACHE_MESSAGE_DATA = ""
)
@@ -25,6 +28,7 @@ type LocalCacheSupplier struct {
next LayeredStoreSupplier
reactionCache *utils.Cache
roleCache *utils.Cache
schemeCache *utils.Cache
metrics einterfaces.MetricsInterface
cluster einterfaces.ClusterInterface
}
@@ -33,6 +37,7 @@ func NewLocalCacheSupplier(metrics einterfaces.MetricsInterface, cluster einterf
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),
schemeCache: utils.NewLruWithParams(SCHEME_CACHE_SIZE, "Scheme", SCHEME_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES),
metrics: metrics,
cluster: cluster,
}

View File

@@ -69,6 +69,17 @@ func (s *LocalCacheSupplier) RoleGetByNames(ctx context.Context, roleNames []str
return result
}
func (s *LocalCacheSupplier) RoleDelete(ctx context.Context, roleId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
result := s.Next().RoleDelete(ctx, roleId, hints...)
if result.Err == nil {
role := result.Data.(*model.Role)
s.doInvalidateCacheCluster(s.roleCache, role.Name)
}
return result
}
func (s *LocalCacheSupplier) RolePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
defer s.roleCache.Purge()
defer s.doClearCacheCluster(s.roleCache)

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2018-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) handleClusterInvalidateScheme(msg *model.ClusterMessage) {
if msg.Data == CLEAR_CACHE_MESSAGE_DATA {
s.schemeCache.Purge()
} else {
s.schemeCache.Remove(msg.Data)
}
}
func (s *LocalCacheSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
if len(scheme.Id) != 0 {
defer s.doInvalidateCacheCluster(s.schemeCache, scheme.Id)
}
return s.Next().SchemeSave(ctx, scheme, hints...)
}
func (s *LocalCacheSupplier) SchemeGet(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
if result := s.doStandardReadCache(ctx, s.schemeCache, schemeId, hints...); result != nil {
return result
}
result := s.Next().SchemeGet(ctx, schemeId, hints...)
s.doStandardAddToCache(ctx, s.schemeCache, schemeId, result, hints...)
return result
}
func (s *LocalCacheSupplier) SchemeDelete(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
defer s.doInvalidateCacheCluster(s.schemeCache, schemeId)
defer s.doClearCacheCluster(s.roleCache)
return s.Next().SchemeDelete(ctx, schemeId, hints...)
}

View File

@@ -84,6 +84,21 @@ func (s *RedisSupplier) RoleGetByNames(ctx context.Context, roleNames []string,
return result
}
func (s *RedisSupplier) RoleDelete(ctx context.Context, roleId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
result := s.Next().RoleGet(ctx, roleId, hints...)
if result.Err == nil {
role := result.Data.(*model.Role)
key := buildRedisKeyForRoleName(role.Name)
if err := s.client.Del(key).Err(); err != nil {
l4g.Error("Redis failed to remove key " + key + " Error: " + err.Error())
}
}
return result
}
func (s *RedisSupplier) RolePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
defer func() {
if keys, err := s.client.Keys("roles:*").Result(); err != nil {

View File

@@ -0,0 +1,25 @@
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package store
import (
"context"
"github.com/mattermost/mattermost-server/model"
)
func (s *RedisSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
// TODO: Redis caching.
return s.Next().SchemeSave(ctx, scheme, hints...)
}
func (s *RedisSupplier) SchemeGet(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
// TODO: Redis caching.
return s.Next().SchemeGet(ctx, schemeId, hints...)
}
func (s *RedisSupplier) SchemeDelete(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
// TODO: Redis caching.
return s.Next().SchemeDelete(ctx, schemeId, hints...)
}

View File

@@ -13,6 +13,7 @@ import (
l4g "github.com/alecthomas/log4go"
"github.com/mattermost/gorp"
"github.com/mattermost/mattermost-server/einterfaces"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
@@ -37,6 +38,200 @@ type SqlChannelStore struct {
metrics einterfaces.MetricsInterface
}
type channelMember struct {
ChannelId string
UserId string
Roles string
LastViewedAt int64
MsgCount int64
MentionCount int64
NotifyProps model.StringMap
LastUpdateAt int64
SchemeUser sql.NullBool
SchemeAdmin sql.NullBool
}
func NewChannelMemberFromModel(cm *model.ChannelMember) *channelMember {
return &channelMember{
ChannelId: cm.ChannelId,
UserId: cm.UserId,
Roles: cm.ExplicitRoles,
LastViewedAt: cm.LastViewedAt,
MsgCount: cm.MsgCount,
MentionCount: cm.MentionCount,
NotifyProps: cm.NotifyProps,
LastUpdateAt: cm.LastUpdateAt,
SchemeUser: sql.NullBool{Valid: true, Bool: cm.SchemeUser},
SchemeAdmin: sql.NullBool{Valid: true, Bool: cm.SchemeAdmin},
}
}
type channelMemberWithSchemeRoles struct {
ChannelId string
UserId string
Roles string
LastViewedAt int64
MsgCount int64
MentionCount int64
NotifyProps model.StringMap
LastUpdateAt int64
SchemeUser sql.NullBool
SchemeAdmin sql.NullBool
TeamSchemeDefaultUserRole sql.NullString
TeamSchemeDefaultAdminRole sql.NullString
ChannelSchemeDefaultUserRole sql.NullString
ChannelSchemeDefaultAdminRole sql.NullString
}
type channelMemberWithSchemeRolesList []channelMemberWithSchemeRoles
func (db channelMemberWithSchemeRoles) ToModel() *model.ChannelMember {
var roles []string
var explicitRoles []string
// Identify any system-wide scheme derived roles that are in "Roles" field due to not yet being migrated,
// and exclude them from ExplicitRoles field.
schemeUser := db.SchemeUser.Valid && db.SchemeUser.Bool
schemeAdmin := db.SchemeAdmin.Valid && db.SchemeAdmin.Bool
for _, role := range strings.Fields(db.Roles) {
isImplicit := false
if role == model.CHANNEL_USER_ROLE_ID {
// We have an implicit role via the system scheme. Override the "schemeUser" field to true.
schemeUser = true
isImplicit = true
} else if role == model.CHANNEL_ADMIN_ROLE_ID {
// We have an implicit role via the system scheme.
schemeAdmin = true
isImplicit = true
}
if !isImplicit {
explicitRoles = append(explicitRoles, role)
}
roles = append(roles, role)
}
// Add any scheme derived roles that are not in the Roles field due to being Implicit from the Scheme, and add
// them to the Roles field for backwards compatibility reasons.
var schemeImpliedRoles []string
if db.SchemeUser.Valid && db.SchemeUser.Bool {
if db.ChannelSchemeDefaultUserRole.Valid && db.ChannelSchemeDefaultUserRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.ChannelSchemeDefaultUserRole.String)
} else if db.TeamSchemeDefaultUserRole.Valid && db.TeamSchemeDefaultUserRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultUserRole.String)
} else {
schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_USER_ROLE_ID)
}
}
if db.SchemeAdmin.Valid && db.SchemeAdmin.Bool {
if db.ChannelSchemeDefaultAdminRole.Valid && db.ChannelSchemeDefaultAdminRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.ChannelSchemeDefaultAdminRole.String)
} else if db.TeamSchemeDefaultAdminRole.Valid && db.TeamSchemeDefaultAdminRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultAdminRole.String)
} else {
schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_ADMIN_ROLE_ID)
}
}
for _, impliedRole := range schemeImpliedRoles {
alreadyThere := false
for _, role := range roles {
if role == impliedRole {
alreadyThere = true
}
}
if !alreadyThere {
roles = append(roles, impliedRole)
}
}
return &model.ChannelMember{
ChannelId: db.ChannelId,
UserId: db.UserId,
Roles: strings.Join(roles, " "),
LastViewedAt: db.LastViewedAt,
MsgCount: db.MsgCount,
MentionCount: db.MentionCount,
NotifyProps: db.NotifyProps,
LastUpdateAt: db.LastUpdateAt,
SchemeAdmin: schemeAdmin,
SchemeUser: schemeUser,
ExplicitRoles: strings.Join(explicitRoles, " "),
}
}
func (db channelMemberWithSchemeRolesList) ToModel() *model.ChannelMembers {
cms := model.ChannelMembers{}
for _, cm := range db {
cms = append(cms, *cm.ToModel())
}
return &cms
}
type allChannelMember struct {
ChannelId string
Roles string
SchemeUser sql.NullBool
SchemeAdmin sql.NullBool
TeamSchemeDefaultUserRole sql.NullString
TeamSchemeDefaultAdminRole sql.NullString
ChannelSchemeDefaultUserRole sql.NullString
ChannelSchemeDefaultAdminRole sql.NullString
}
type allChannelMembers []allChannelMember
func (db allChannelMember) Process() (string, string) {
roles := strings.Fields(db.Roles)
// Add any scheme derived roles that are not in the Roles field due to being Implicit from the Scheme, and add
// them to the Roles field for backwards compatibility reasons.
var schemeImpliedRoles []string
if db.SchemeUser.Valid && db.SchemeUser.Bool {
if db.ChannelSchemeDefaultUserRole.Valid && db.ChannelSchemeDefaultUserRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.ChannelSchemeDefaultUserRole.String)
} else if db.TeamSchemeDefaultUserRole.Valid && db.TeamSchemeDefaultUserRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultUserRole.String)
} else {
schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_USER_ROLE_ID)
}
}
if db.SchemeAdmin.Valid && db.SchemeAdmin.Bool {
if db.ChannelSchemeDefaultAdminRole.Valid && db.ChannelSchemeDefaultAdminRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.ChannelSchemeDefaultAdminRole.String)
} else if db.TeamSchemeDefaultAdminRole.Valid && db.TeamSchemeDefaultAdminRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultAdminRole.String)
} else {
schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_ADMIN_ROLE_ID)
}
}
for _, impliedRole := range schemeImpliedRoles {
alreadyThere := false
for _, role := range roles {
if role == impliedRole {
alreadyThere = true
}
}
if !alreadyThere {
roles = append(roles, impliedRole)
}
}
return db.ChannelId, strings.Join(roles, " ")
}
func (db allChannelMembers) ToMapStringString() map[string]string {
result := make(map[string]string)
for _, item := range db {
key, value := item.Process()
result[key] = value
}
return result
}
var channelMemberCountsCache = utils.NewLru(CHANNEL_MEMBERS_COUNTS_CACHE_SIZE)
var allChannelMembersForUserCache = utils.NewLru(ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE)
var allChannelMembersNotifyPropsForChannelCache = utils.NewLru(ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SIZE)
@@ -76,8 +271,9 @@ func NewSqlChannelStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface)
table.ColMap("Header").SetMaxSize(1024)
table.ColMap("Purpose").SetMaxSize(250)
table.ColMap("CreatorId").SetMaxSize(26)
table.ColMap("SchemeId").SetMaxSize(26)
tablem := db.AddTableWithName(model.ChannelMember{}, "ChannelMembers").SetKeys(false, "ChannelId", "UserId")
tablem := db.AddTableWithName(channelMember{}, "ChannelMembers").SetKeys(false, "ChannelId", "UserId")
tablem.ColMap("ChannelId").SetMaxSize(26)
tablem.ColMap("UserId").SetMaxSize(26)
tablem.ColMap("Roles").SetMaxSize(64)
@@ -138,12 +334,12 @@ func (s SqlChannelStore) CreateDirectChannel(userId string, otherUserId string)
cm1 := &model.ChannelMember{
UserId: userId,
NotifyProps: model.GetDefaultChannelNotifyProps(),
Roles: model.CHANNEL_USER_ROLE_ID,
SchemeUser: true,
}
cm2 := &model.ChannelMember{
UserId: otherUserId,
NotifyProps: model.GetDefaultChannelNotifyProps(),
Roles: model.CHANNEL_USER_ROLE_ID,
SchemeUser: true,
}
return s.SaveDirectChannel(channel, cm1, cm2)
@@ -732,6 +928,25 @@ func (s SqlChannelStore) GetDeleted(teamId string, offset int, limit int) store.
})
}
var CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY = `
SELECT
ChannelMembers.*,
TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole,
TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole,
ChannelScheme.DefaultChannelUserRole ChannelSchemeDefaultUserRole,
ChannelScheme.DefaultChannelAdminRole ChannelSchemeDefaultAdminRole
FROM
ChannelMembers
INNER JOIN
Channels ON ChannelMembers.ChannelId = Channels.Id
LEFT JOIN
Schemes ChannelScheme ON Channels.SchemeId = ChannelScheme.Id
LEFT JOIN
Teams ON Channels.TeamId = Teams.Id
LEFT JOIN
Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id
`
func (s SqlChannelStore) SaveMember(member *model.ChannelMember) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
// Grab the channel we are saving this member to
@@ -750,7 +965,7 @@ func (s SqlChannelStore) SaveMember(member *model.ChannelMember) store.StoreChan
if err := transaction.Commit(); err != nil {
result.Err = model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
// If successfull record members have changed in channel
// If successful record members have changed in channel
if mu := <-s.extraUpdated(channel); mu.Err != nil {
result.Err = mu.Err
}
@@ -770,14 +985,25 @@ func (s SqlChannelStore) saveMemberT(transaction *gorp.Transaction, member *mode
return result
}
if err := transaction.Insert(member); err != nil {
dbMember := NewChannelMemberFromModel(member)
if err := transaction.Insert(dbMember); err != nil {
if IsUniqueConstraintError(err, []string{"ChannelId", "channelmembers_pkey"}) {
result.Err = model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.exists.app_error", nil, "channel_id="+member.ChannelId+", user_id="+member.UserId+", "+err.Error(), http.StatusBadRequest)
} else {
result.Err = model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.save.app_error", nil, "channel_id="+member.ChannelId+", user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError)
}
} else {
result.Data = member
var retrievedMember channelMemberWithSchemeRoles
if err := transaction.SelectOne(&retrievedMember, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId = :UserId", map[string]interface{}{"ChannelId": dbMember.ChannelId, "UserId": dbMember.UserId}); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlChannelStore.GetMember", store.MISSING_CHANNEL_MEMBER_ERROR, nil, "channel_id="+dbMember.ChannelId+"user_id="+dbMember.UserId+","+err.Error(), http.StatusNotFound)
} else {
result.Err = model.NewAppError("SqlChannelStore.GetMember", "store.sql_channel.get_member.app_error", nil, "channel_id="+dbMember.ChannelId+"user_id="+dbMember.UserId+","+err.Error(), http.StatusInternalServerError)
}
} else {
result.Data = retrievedMember.ToModel()
}
}
return result
@@ -791,38 +1017,48 @@ func (s SqlChannelStore) UpdateMember(member *model.ChannelMember) store.StoreCh
return
}
if _, err := s.GetMaster().Update(member); err != nil {
if _, err := s.GetMaster().Update(NewChannelMemberFromModel(member)); err != nil {
result.Err = model.NewAppError("SqlChannelStore.UpdateMember", "store.sql_channel.update_member.app_error", nil, "channel_id="+member.ChannelId+", "+"user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError)
} else {
result.Data = member
var dbMember channelMemberWithSchemeRoles
if err := s.GetReplica().SelectOne(&dbMember, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId = :UserId", map[string]interface{}{"ChannelId": member.ChannelId, "UserId": member.UserId}); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlChannelStore.GetMember", store.MISSING_CHANNEL_MEMBER_ERROR, nil, "channel_id="+member.ChannelId+"user_id="+member.UserId+","+err.Error(), http.StatusNotFound)
} else {
result.Err = model.NewAppError("SqlChannelStore.GetMember", "store.sql_channel.get_member.app_error", nil, "channel_id="+member.ChannelId+"user_id="+member.UserId+","+err.Error(), http.StatusInternalServerError)
}
} else {
result.Data = dbMember.ToModel()
}
}
})
}
func (s SqlChannelStore) GetMembers(channelId string, offset, limit int) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var members model.ChannelMembers
_, err := s.GetReplica().Select(&members, "SELECT * FROM ChannelMembers WHERE ChannelId = :ChannelId LIMIT :Limit OFFSET :Offset", map[string]interface{}{"ChannelId": channelId, "Limit": limit, "Offset": offset})
var dbMembers channelMemberWithSchemeRolesList
_, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelId = :ChannelId LIMIT :Limit OFFSET :Offset", map[string]interface{}{"ChannelId": channelId, "Limit": limit, "Offset": offset})
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetMembers", "store.sql_channel.get_members.app_error", nil, "channel_id="+channelId+err.Error(), http.StatusInternalServerError)
} else {
result.Data = &members
result.Data = dbMembers.ToModel()
}
})
}
func (s SqlChannelStore) GetMember(channelId string, userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var member model.ChannelMember
var dbMember channelMemberWithSchemeRoles
if err := s.GetReplica().SelectOne(&member, "SELECT * FROM ChannelMembers WHERE ChannelId = :ChannelId AND UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId}); err != nil {
if err := s.GetReplica().SelectOne(&dbMember, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId}); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlChannelStore.GetMember", store.MISSING_CHANNEL_MEMBER_ERROR, nil, "channel_id="+channelId+"user_id="+userId+","+err.Error(), http.StatusNotFound)
} else {
result.Err = model.NewAppError("SqlChannelStore.GetMember", "store.sql_channel.get_member.app_error", nil, "channel_id="+channelId+"user_id="+userId+","+err.Error(), http.StatusInternalServerError)
}
} else {
result.Data = &member
result.Data = dbMember.ToModel()
}
})
}
@@ -866,30 +1102,37 @@ func (s SqlChannelStore) IsUserInChannelUseCache(userId string, channelId string
func (s SqlChannelStore) GetMemberForPost(postId string, userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
member := &model.ChannelMember{}
if err := s.GetReplica().SelectOne(
member,
`SELECT
ChannelMembers.*
FROM
ChannelMembers,
Posts
var dbMember channelMemberWithSchemeRoles
if err := s.GetReplica().SelectOne(&dbMember,
`
SELECT
ChannelMembers.*,
TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole,
TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole,
ChannelScheme.DefaultChannelUserRole ChannelSchemeDefaultUserRole,
ChannelScheme.DefaultChannelAdminRole ChannelSchemeDefaultAdminRole
FROM
ChannelMembers
INNER JOIN
Posts ON ChannelMembers.ChannelId = Posts.ChannelId
INNER JOIN
Channels ON ChannelMembers.ChannelId = Channels.Id
LEFT JOIN
Schemes ChannelScheme ON Channels.SchemeId = ChannelScheme.Id
LEFT JOIN
Teams ON Channels.TeamId = Teams.Id
LEFT JOIN
Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id
WHERE
ChannelMembers.ChannelId = Posts.ChannelId
AND ChannelMembers.UserId = :UserId
ChannelMembers.UserId = :UserId
AND Posts.Id = :PostId`, map[string]interface{}{"UserId": userId, "PostId": postId}); err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetMemberForPost", "store.sql_channel.get_member_for_post.app_error", nil, "postId="+postId+", err="+err.Error(), http.StatusInternalServerError)
} else {
result.Data = member
result.Data = dbMember.ToModel()
}
})
}
type allChannelMember struct {
ChannelId string
Roles string
}
func (s SqlChannelStore) GetAllChannelMembersForUser(userId string, allowFromCache bool) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
if allowFromCache {
@@ -910,17 +1153,32 @@ func (s SqlChannelStore) GetAllChannelMembersForUser(userId string, allowFromCac
}
}
var data []allChannelMember
_, err := s.GetReplica().Select(&data, "SELECT ChannelId, Roles FROM Channels, ChannelMembers WHERE Channels.Id = ChannelMembers.ChannelId AND ChannelMembers.UserId = :UserId AND Channels.DeleteAt = 0", map[string]interface{}{"UserId": userId})
var data allChannelMembers
_, err := s.GetReplica().Select(&data, `
SELECT
ChannelMembers.ChannelId, ChannelMembers.Roles, ChannelMembers.SchemeUser, ChannelMembers.SchemeAdmin,
TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole,
TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole,
ChannelScheme.DefaultChannelUserRole ChannelSchemeDefaultUserRole,
ChannelScheme.DefaultChannelAdminRole ChannelSchemeDefaultAdminRole
FROM
ChannelMembers
INNER JOIN
Channels ON ChannelMembers.ChannelId = Channels.Id
LEFT JOIN
Schemes ChannelScheme ON Channels.SchemeId = ChannelScheme.Id
LEFT JOIN
Teams ON Channels.TeamId = Teams.Id
LEFT JOIN
Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id
WHERE
Channels.DeleteAt = 0
AND ChannelMembers.UserId = :UserId`, map[string]interface{}{"UserId": userId})
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetAllChannelMembersForUser", "store.sql_channel.get_channels.get.app_error", nil, "userId="+userId+", err="+err.Error(), http.StatusInternalServerError)
} else {
ids := make(map[string]string)
for i := range data {
ids[data[i].ChannelId] = data[i].Roles
}
ids := data.ToMapStringString()
result.Data = ids
@@ -1249,21 +1507,13 @@ func (s SqlChannelStore) AnalyticsDeletedTypeCount(teamId string, channelType st
func (s SqlChannelStore) GetMembersForUser(teamId string, userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
members := &model.ChannelMembers{}
_, err := s.GetReplica().Select(members, `
SELECT cm.*
FROM ChannelMembers cm
INNER JOIN Channels c
ON c.Id = cm.ChannelId
AND (c.TeamId = :TeamId OR c.TeamId = '')
AND c.DeleteAt = 0
WHERE cm.UserId = :UserId
`, map[string]interface{}{"TeamId": teamId, "UserId": userId})
var dbMembers channelMemberWithSchemeRolesList
_, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.UserId = :UserId", map[string]interface{}{"TeamId": teamId, "UserId": userId})
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetMembersForUser", "store.sql_channel.get_members.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError)
} else {
result.Data = members
result.Data = dbMembers.ToModel()
}
})
}
@@ -1455,7 +1705,7 @@ func (s SqlChannelStore) performSearch(searchQuery string, term string, paramete
func (s SqlChannelStore) GetMembersByIds(channelId string, userIds []string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var members model.ChannelMembers
var dbMembers channelMemberWithSchemeRolesList
props := make(map[string]interface{})
idQuery := ""
@@ -1470,11 +1720,22 @@ func (s SqlChannelStore) GetMembersByIds(channelId string, userIds []string) sto
props["ChannelId"] = channelId
if _, err := s.GetReplica().Select(&members, "SELECT * FROM ChannelMembers WHERE ChannelId = :ChannelId AND UserId IN ("+idQuery+")", props); err != nil {
if _, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId IN ("+idQuery+")", props); err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetMembersByIds", "store.sql_channel.get_members_by_ids.app_error", nil, "channelId="+channelId+" "+err.Error(), http.StatusInternalServerError)
} else {
result.Data = &members
result.Data = dbMembers.ToModel()
}
})
}
func (s SqlChannelStore) GetChannelsByScheme(schemeId string, offset int, limit int) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var channels []*model.Channel
_, err := s.GetReplica().Select(&channels, "SELECT * FROM Channels WHERE SchemeId = :SchemeId ORDER BY DisplayName LIMIT :Limit OFFSET :Offset", map[string]interface{}{"SchemeId": schemeId, "Offset": offset, "Limit": limit})
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetChannelsByScheme", "store.sql_channel.get_by_scheme.app_error", nil, "schemeId="+schemeId+" "+err.Error(), http.StatusInternalServerError)
} else {
result.Data = channels
}
})
}

View File

@@ -4,11 +4,937 @@
package sqlstore
import (
"database/sql"
"testing"
"github.com/stretchr/testify/assert"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store/storetest"
)
func TestChannelStore(t *testing.T) {
StoreTest(t, storetest.TestChannelStore)
}
func TestChannelStoreInternalDataTypes(t *testing.T) {
t.Run("NewChannelMemberFromModel", func(t *testing.T) { testNewChannelMemberFromModel(t) })
t.Run("ChannelMemberWithSchemeRolesToModel", func(t *testing.T) { testChannelMemberWithSchemeRolesToModel(t) })
t.Run("AllChannelMemberProcess", func(t *testing.T) { testAllChannelMemberProcess(t) })
}
func testNewChannelMemberFromModel(t *testing.T) {
m := model.ChannelMember{
ChannelId: model.NewId(),
UserId: model.NewId(),
Roles: "channel_user channel_admin custom_role",
LastViewedAt: 12345,
MsgCount: 2,
MentionCount: 1,
NotifyProps: model.StringMap{"key": "value"},
LastUpdateAt: 54321,
SchemeUser: true,
SchemeAdmin: true,
ExplicitRoles: "custom_role",
}
db := NewChannelMemberFromModel(&m)
assert.Equal(t, m.ChannelId, db.ChannelId)
assert.Equal(t, m.UserId, db.UserId)
assert.Equal(t, m.LastViewedAt, db.LastViewedAt)
assert.Equal(t, m.MsgCount, db.MsgCount)
assert.Equal(t, m.MentionCount, db.MentionCount)
assert.Equal(t, m.NotifyProps, db.NotifyProps)
assert.Equal(t, m.LastUpdateAt, db.LastUpdateAt)
assert.Equal(t, true, db.SchemeUser.Valid)
assert.Equal(t, true, db.SchemeAdmin.Valid)
assert.Equal(t, m.SchemeUser, db.SchemeUser.Bool)
assert.Equal(t, m.SchemeAdmin, db.SchemeAdmin.Bool)
assert.Equal(t, m.ExplicitRoles, db.Roles)
}
func testChannelMemberWithSchemeRolesToModel(t *testing.T) {
t.Run("BasicProperties", func(t *testing.T) {
// Test all the non-roles properties here.
db := channelMemberWithSchemeRoles{
ChannelId: model.NewId(),
UserId: model.NewId(),
Roles: "custom_role",
LastViewedAt: 12345,
MsgCount: 2,
MentionCount: 1,
NotifyProps: model.StringMap{"key": "value"},
LastUpdateAt: 54321,
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, db.ChannelId, m.ChannelId)
assert.Equal(t, db.UserId, m.UserId)
assert.Equal(t, "custom_role channel_user channel_admin", m.Roles)
assert.Equal(t, db.LastViewedAt, m.LastViewedAt)
assert.Equal(t, db.MsgCount, m.MsgCount)
assert.Equal(t, db.MentionCount, m.MentionCount)
assert.Equal(t, db.NotifyProps, m.NotifyProps)
assert.Equal(t, db.LastUpdateAt, m.LastUpdateAt)
assert.Equal(t, db.SchemeUser.Bool, m.SchemeUser)
assert.Equal(t, db.SchemeAdmin.Bool, m.SchemeAdmin)
assert.Equal(t, db.Roles, m.ExplicitRoles)
})
// Example data *before* the Phase 2 migration has taken place.
t.Run("Unmigrated_NoScheme_User", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "channel_user",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "channel_user", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_Admin", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "channel_admin channel_user",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "channel_admin channel_user", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_CustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "custom_role", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_UserAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "channel_user custom_role",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "channel_user custom_role", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_AdminAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "channel_user channel_admin custom_role",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "channel_user channel_admin custom_role", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_NoRoles", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
// Example data *after* the Phase 2 migration has taken place.
t.Run("Migrated_NoScheme_User", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "channel_user", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Migrated_NoScheme_Admin", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "channel_user channel_admin", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Migrated_NoScheme_CustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "custom_role", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_NoScheme_UserAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "custom_role channel_user", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_NoScheme_AdminAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "custom_role channel_user channel_admin", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_NoScheme_NoRoles", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
// Example data with a channel scheme.
t.Run("Migrated_ChannelScheme_User", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "cscheme_user", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Migrated_ChannelScheme_Admin", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "cscheme_user cscheme_admin", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Migrated_ChannelScheme_CustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "custom_role", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_ChannelScheme_UserAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "custom_role cscheme_user", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_ChannelScheme_AdminAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "custom_role cscheme_user cscheme_admin", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_ChannelScheme_NoRoles", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
// Example data with a team scheme.
t.Run("Migrated_TeamScheme_User", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "tscheme_channeluser", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_Admin", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "tscheme_channeluser tscheme_channeladmin", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_CustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "custom_role", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_UserAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "custom_role tscheme_channeluser", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_AdminAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "custom_role tscheme_channeluser tscheme_channeladmin", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_NoRoles", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
// Example data with a team and channel scheme.
t.Run("Migrated_TeamAndChannelScheme_User", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "cscheme_user", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Migrated_TeamAndChannelScheme_Admin", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "cscheme_user cscheme_admin", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Migrated_TeamAndChannelScheme_CustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "custom_role", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_TeamAndChannelScheme_UserAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "custom_role cscheme_user", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_TeamAndChannelScheme_AdminAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "custom_role cscheme_user cscheme_admin", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_TeamAndChannelScheme_NoRoles", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
}
func testAllChannelMemberProcess(t *testing.T) {
t.Run("Unmigrated_User", func(t *testing.T) {
db := allChannelMember{
Roles: "channel_user",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "channel_user", roles)
})
t.Run("Unmigrated_Admin", func(t *testing.T) {
db := allChannelMember{
Roles: "channel_user channel_admin",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "channel_user channel_admin", roles)
})
t.Run("Unmigrated_Neither", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "", roles)
})
t.Run("Unmigrated_Custom", func(t *testing.T) {
db := allChannelMember{
Roles: "custom",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "custom", roles)
})
t.Run("MigratedNoScheme_User", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "channel_user", roles)
})
t.Run("MigratedNoScheme_Admin", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "channel_user channel_admin", roles)
})
t.Run("MigratedNoScheme_Neither", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "", roles)
})
t.Run("MigratedChannelScheme_User", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
_, roles := db.Process()
assert.Equal(t, "cscheme_user", roles)
})
t.Run("MigratedChannelScheme_Admin", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
_, roles := db.Process()
assert.Equal(t, "cscheme_user cscheme_admin", roles)
})
t.Run("MigratedChannelScheme_Neither", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
_, roles := db.Process()
assert.Equal(t, "", roles)
})
t.Run("MigratedTeamScheme_User", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "tscheme_channeluser", roles)
})
t.Run("MigratedTeamScheme_Admin", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "tscheme_channeluser tscheme_channeladmin", roles)
})
t.Run("MigratedTeamScheme_Neither", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "", roles)
})
t.Run("MigratedTeamAndChannelScheme_User", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
_, roles := db.Process()
assert.Equal(t, "cscheme_user", roles)
})
t.Run("MigratedTeamAndChannelScheme_Admin", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
_, roles := db.Process()
assert.Equal(t, "cscheme_user cscheme_admin", roles)
})
t.Run("MigratedTeamAndChannelScheme_Neither", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
_, roles := db.Process()
assert.Equal(t, "", roles)
})
t.Run("DeduplicationCheck", func(t *testing.T) {
db := allChannelMember{
Roles: "channel_user",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "channel_user", roles)
})
}

View File

@@ -10,6 +10,8 @@ import (
"net/http"
"strings"
"github.com/mattermost/gorp"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
@@ -24,6 +26,7 @@ type Role struct {
DeleteAt int64
Permissions string
SchemeManaged bool
BuiltIn bool
}
func NewRoleFromModel(role *model.Role) *Role {
@@ -47,6 +50,7 @@ func NewRoleFromModel(role *model.Role) *Role {
DeleteAt: role.DeleteAt,
Permissions: permissions,
SchemeManaged: role.SchemeManaged,
BuiltIn: role.BuiltIn,
}
}
@@ -61,6 +65,7 @@ func (role Role) ToModel() *model.Role {
DeleteAt: role.DeleteAt,
Permissions: strings.Fields(role.Permissions),
SchemeManaged: role.SchemeManaged,
BuiltIn: role.BuiltIn,
}
}
@@ -84,21 +89,52 @@ func (s *SqlSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...s
return result
}
dbRole := NewRoleFromModel(role)
if len(dbRole.Id) == 0 {
dbRole.Id = model.NewId()
dbRole.CreateAt = model.GetMillis()
dbRole.UpdateAt = dbRole.CreateAt
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)
if len(role.Id) == 0 {
if transaction, err := s.GetMaster().Begin(); err != nil {
result.Err = model.NewAppError("SqlRoleStore.RoleSave", "store.sql_role.save.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
return result
} else {
result = s.createRole(ctx, role, transaction, hints...)
if result.Err != nil {
transaction.Rollback()
} else if err := transaction.Commit(); err != nil {
result.Err = model.NewAppError("SqlRoleStore.RoleSave", "store.sql_role.save_role.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
}
} else {
dbRole := NewRoleFromModel(role)
dbRole.UpdateAt = model.GetMillis()
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) createRole(ctx context.Context, role *model.Role, transaction *gorp.Transaction, 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)
dbRole.Id = model.NewId()
dbRole.CreateAt = model.GetMillis()
dbRole.UpdateAt = dbRole.CreateAt
if err := transaction.Insert(dbRole); err != nil {
result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.insert.app_error", nil, err.Error(), http.StatusInternalServerError)
}
result.Data = dbRole.ToModel()
@@ -175,6 +211,36 @@ func (s *SqlSupplier) RoleGetByNames(ctx context.Context, names []string, hints
return result
}
func (s *SqlSupplier) RoleDelete(ctx context.Context, roleId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()
// Get the role.
var role *Role
if err := s.GetReplica().SelectOne(&role, "SELECT * from Roles WHERE Id = :Id", map[string]interface{}{"Id": roleId}); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlRoleStore.Delete", "store.sql_role.get.app_error", nil, "Id="+roleId+", "+err.Error(), http.StatusNotFound)
} else {
result.Err = model.NewAppError("SqlRoleStore.Delete", "store.sql_role.get.app_error", nil, err.Error(), http.StatusInternalServerError)
}
return result
}
time := model.GetMillis()
role.DeleteAt = time
role.UpdateAt = time
if rowsChanged, err := s.GetMaster().Update(role); err != nil {
result.Err = model.NewAppError("SqlRoleStore.Delete", "store.sql_role.delete.update.app_error", nil, err.Error(), http.StatusInternalServerError)
} else if rowsChanged != 1 {
result.Err = model.NewAppError("SqlRoleStore.Delete", "store.sql_role.delete.update.app_error", nil, "no record to update", http.StatusInternalServerError)
} else {
result.Data = role.ToModel()
}
return result
}
func (s *SqlSupplier) RolePermanentDeleteAll(ctx context.Context, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()

View File

@@ -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 TestSchemeStore(t *testing.T) {
StoreTest(t, storetest.TestSchemeStore)
}

View File

@@ -0,0 +1,272 @@
// Copyright (c) 2018-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/gorp"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
func initSqlSupplierSchemes(sqlStore SqlStore) {
for _, db := range sqlStore.GetAllConns() {
table := db.AddTableWithName(model.Scheme{}, "Schemes").SetKeys(false, "Id")
table.ColMap("Id").SetMaxSize(26)
table.ColMap("Name").SetMaxSize(64)
table.ColMap("Description").SetMaxSize(1024)
table.ColMap("Scope").SetMaxSize(32)
table.ColMap("DefaultTeamAdminRole").SetMaxSize(64)
table.ColMap("DefaultTeamUserRole").SetMaxSize(64)
table.ColMap("DefaultChannelAdminRole").SetMaxSize(64)
table.ColMap("DefaultChannelUserRole").SetMaxSize(64)
}
}
func (s *SqlSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()
if len(scheme.Id) == 0 {
if transaction, err := s.GetMaster().Begin(); err != nil {
result.Err = model.NewAppError("SqlSchemeStore.SaveScheme", "store.sql_scheme.save.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
} else {
result = s.createScheme(ctx, scheme, transaction, hints...)
if result.Err != nil {
transaction.Rollback()
} else if err := transaction.Commit(); err != nil {
result.Err = model.NewAppError("SqlSchemeStore.SchemeSave", "store.sql_scheme.save_scheme.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
}
} else {
if !scheme.IsValid() {
result.Err = model.NewAppError("SqlSchemeStore.Save", "store.sql_scheme.save.invalid_scheme.app_error", nil, "", http.StatusBadRequest)
return result
}
scheme.UpdateAt = model.GetMillis()
if rowsChanged, err := s.GetMaster().Update(scheme); err != nil {
result.Err = model.NewAppError("SqlSchemeStore.Save", "store.sql_scheme.save.update.app_error", nil, err.Error(), http.StatusInternalServerError)
} else if rowsChanged != 1 {
result.Err = model.NewAppError("SqlSchemeStore.Save", "store.sql_scheme.save.update.app_error", nil, "no record to update", http.StatusInternalServerError)
}
result.Data = scheme
}
return result
}
func (s *SqlSupplier) createScheme(ctx context.Context, scheme *model.Scheme, transaction *gorp.Transaction, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()
// Fetch the default system scheme roles to populate default permissions.
defaultRoleNames := []string{model.TEAM_ADMIN_ROLE_ID, model.TEAM_USER_ROLE_ID, model.CHANNEL_ADMIN_ROLE_ID, model.CHANNEL_USER_ROLE_ID}
defaultRoles := make(map[string]*model.Role)
if rolesResult := s.RoleGetByNames(ctx, defaultRoleNames); rolesResult.Err != nil {
result.Err = rolesResult.Err
return result
} else {
for _, role := range rolesResult.Data.([]*model.Role) {
switch role.Name {
case model.TEAM_ADMIN_ROLE_ID:
defaultRoles[model.TEAM_ADMIN_ROLE_ID] = role
case model.TEAM_USER_ROLE_ID:
defaultRoles[model.TEAM_USER_ROLE_ID] = role
case model.CHANNEL_ADMIN_ROLE_ID:
defaultRoles[model.CHANNEL_ADMIN_ROLE_ID] = role
case model.CHANNEL_USER_ROLE_ID:
defaultRoles[model.CHANNEL_USER_ROLE_ID] = role
}
}
if len(defaultRoles) != 4 {
result.Err = model.NewAppError("SqlSchemeStore.SaveScheme", "store.sql_scheme.save.retrieve_default_scheme_roles.app_error", nil, "", http.StatusInternalServerError)
return result
}
}
// Create the appropriate default roles for the scheme.
if scheme.Scope == model.SCHEME_SCOPE_TEAM {
// Team Admin Role
teamAdminRole := &model.Role{
Name: model.NewId(),
DisplayName: fmt.Sprintf("Team Admin Role for Scheme %s", scheme.Name),
Permissions: defaultRoles[model.TEAM_ADMIN_ROLE_ID].Permissions,
SchemeManaged: true,
}
if saveRoleResult := s.createRole(ctx, teamAdminRole, transaction); saveRoleResult.Err != nil {
result.Err = saveRoleResult.Err
return result
} else {
scheme.DefaultTeamAdminRole = saveRoleResult.Data.(*model.Role).Id
}
// Team User Role
teamUserRole := &model.Role{
Name: model.NewId(),
DisplayName: fmt.Sprintf("Team User Role for Scheme %s", scheme.Name),
Permissions: defaultRoles[model.TEAM_USER_ROLE_ID].Permissions,
SchemeManaged: true,
}
if saveRoleResult := s.createRole(ctx, teamUserRole, transaction); saveRoleResult.Err != nil {
result.Err = saveRoleResult.Err
return result
} else {
scheme.DefaultTeamUserRole = saveRoleResult.Data.(*model.Role).Id
}
}
if scheme.Scope == model.SCHEME_SCOPE_TEAM || scheme.Scope == model.SCHEME_SCOPE_CHANNEL {
// Channel Admin Role
channelAdminRole := &model.Role{
Name: model.NewId(),
DisplayName: fmt.Sprintf("Channel Admin Role for Scheme %s", scheme.Name),
Permissions: defaultRoles[model.CHANNEL_ADMIN_ROLE_ID].Permissions,
SchemeManaged: true,
}
if saveRoleResult := s.createRole(ctx, channelAdminRole, transaction); saveRoleResult.Err != nil {
result.Err = saveRoleResult.Err
return result
} else {
scheme.DefaultChannelAdminRole = saveRoleResult.Data.(*model.Role).Id
}
// Channel User Role
channelUserRole := &model.Role{
Name: model.NewId(),
DisplayName: fmt.Sprintf("Channel User Role for Scheme %s", scheme.Name),
Permissions: defaultRoles[model.CHANNEL_USER_ROLE_ID].Permissions,
SchemeManaged: true,
}
if saveRoleResult := s.createRole(ctx, channelUserRole, transaction); saveRoleResult.Err != nil {
result.Err = saveRoleResult.Err
return result
} else {
scheme.DefaultChannelUserRole = saveRoleResult.Data.(*model.Role).Id
}
}
scheme.Id = model.NewId()
scheme.CreateAt = model.GetMillis()
scheme.UpdateAt = scheme.CreateAt
// Validate the scheme
if !scheme.IsValidForCreate() {
result.Err = model.NewAppError("SqlSchemeStore.Save", "store.sql_scheme.save.invalid_scheme.app_error", nil, "", http.StatusBadRequest)
return result
}
if err := transaction.Insert(scheme); err != nil {
result.Err = model.NewAppError("SqlSchemeStore.Save", "store.sql_scheme.save.insert.app_error", nil, err.Error(), http.StatusInternalServerError)
}
result.Data = scheme
return result
}
func (s *SqlSupplier) SchemeGet(ctx context.Context, schemeId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()
var scheme model.Scheme
if err := s.GetReplica().SelectOne(&scheme, "SELECT * from Schemes WHERE Id = :Id", map[string]interface{}{"Id": schemeId}); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlSchemeStore.Get", "store.sql_scheme.get.app_error", nil, "Id="+schemeId+", "+err.Error(), http.StatusNotFound)
} else {
result.Err = model.NewAppError("SqlSchemeStore.Get", "store.sql_scheme.get.app_error", nil, err.Error(), http.StatusInternalServerError)
}
}
result.Data = &scheme
return result
}
func (s *SqlSupplier) SchemeDelete(ctx context.Context, schemeId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()
// Get the scheme
var scheme model.Scheme
if err := s.GetReplica().SelectOne(&scheme, "SELECT * from Schemes WHERE Id = :Id", map[string]interface{}{"Id": schemeId}); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.get.app_error", nil, "Id="+schemeId+", "+err.Error(), http.StatusNotFound)
} else {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.get.app_error", nil, "Id="+schemeId+", "+err.Error(), http.StatusInternalServerError)
}
return result
}
// Check that the scheme isn't being used on any Teams or Channels.
if scheme.Scope == model.SCHEME_SCOPE_TEAM {
if c, err := s.GetReplica().SelectInt("SELECT COUNT(*) FROM Teams WHERE SchemeId = :SchemeId", map[string]interface{}{"SchemeId": schemeId}); err != nil {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.team_count.app_error", nil, "Id="+schemeId+", "+err.Error(), http.StatusInternalServerError)
return result
} else {
if c > 0 {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.delete.scheme_in_use.app_error", nil, "Id="+schemeId, http.StatusInternalServerError)
return result
}
}
} else if scheme.Scope == model.SCHEME_SCOPE_CHANNEL {
if c, err := s.GetReplica().SelectInt("SELECT COUNT(*) FROM Channels WHERE SchemeId = :SchemeId", map[string]interface{}{"SchemeId": schemeId}); err != nil {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.channel_count.app_error", nil, "Id="+schemeId+", "+err.Error(), http.StatusInternalServerError)
return result
} else {
if c > 0 {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.delete.scheme_in_use.app_error", nil, "Id="+schemeId, http.StatusInternalServerError)
return result
}
}
}
// Delete the roles belonging to the scheme.
roleIds := []string{scheme.DefaultChannelUserRole, scheme.DefaultChannelAdminRole}
if scheme.Scope == model.SCHEME_SCOPE_TEAM {
roleIds = append(roleIds, scheme.DefaultTeamUserRole, scheme.DefaultTeamAdminRole)
}
var inQueryList []string
queryArgs := make(map[string]interface{})
for i, roleId := range roleIds {
inQueryList = append(inQueryList, fmt.Sprintf(":RoleId%v", i))
queryArgs[fmt.Sprintf("RoleId%v", i)] = roleId
}
inQuery := strings.Join(inQueryList, ", ")
time := model.GetMillis()
queryArgs["UpdateAt"] = time
queryArgs["DeleteAt"] = time
if _, err := s.GetMaster().Exec("UPDATE Roles SET UpdateAt = :UpdateAt, DeleteAt = :DeleteAt WHERE Id IN ("+inQuery+")", queryArgs); err != nil {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.delete.role_update.app_error", nil, "Id="+schemeId+", "+err.Error(), http.StatusInternalServerError)
return result
}
// Delete the scheme itself.
scheme.UpdateAt = time
scheme.DeleteAt = time
if rowsChanged, err := s.GetMaster().Update(&scheme); err != nil {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.delete.update.app_error", nil, "Id="+schemeId+", "+err.Error(), http.StatusInternalServerError)
} else if rowsChanged != 1 {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.delete.update.app_error", nil, "no record to update", http.StatusInternalServerError)
} else {
result.Data = &scheme
}
return result
}

View File

@@ -52,6 +52,7 @@ type SqlStore interface {
DoesTableExist(tablename string) bool
DoesColumnExist(tableName string, columName string) bool
CreateColumnIfNotExists(tableName string, columnName string, mySqlColType string, postgresColType string, defaultValue string) bool
CreateColumnIfNotExistsNoDefault(tableName string, columnName string, mySqlColType string, postgresColType string) bool
RemoveColumnIfExists(tableName string, columnName string) bool
RemoveTableIfExists(tableName string) bool
RenameColumnIfExists(tableName string, oldColumnName string, newColumnName string, colType string) bool
@@ -88,4 +89,5 @@ type SqlStore interface {
Plugin() store.PluginStore
UserAccessToken() store.UserAccessTokenStore
Role() store.RoleStore
Scheme() store.SchemeStore
}

View File

@@ -89,6 +89,7 @@ type SqlSupplierOldStores struct {
plugin store.PluginStore
channelMemberHistory store.ChannelMemberHistoryStore
role store.RoleStore
scheme store.SchemeStore
}
type SqlSupplier struct {
@@ -139,6 +140,7 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter
initSqlSupplierReactions(supplier)
initSqlSupplierRoles(supplier)
initSqlSupplierSchemes(supplier)
err := supplier.GetMaster().CreateTablesIfNotExists()
if err != nil {
@@ -462,6 +464,40 @@ func (ss *SqlSupplier) CreateColumnIfNotExists(tableName string, columnName stri
}
}
func (ss *SqlSupplier) CreateColumnIfNotExistsNoDefault(tableName string, columnName string, mySqlColType string, postgresColType string) bool {
if ss.DoesColumnExist(tableName, columnName) {
return false
}
if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
_, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ADD " + columnName + " " + postgresColType)
if err != nil {
l4g.Critical(utils.T("store.sql.create_column.critical"), err)
time.Sleep(time.Second)
os.Exit(EXIT_CREATE_COLUMN_POSTGRES)
}
return true
} else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
_, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ADD " + columnName + " " + mySqlColType)
if err != nil {
l4g.Critical(utils.T("store.sql.create_column.critical"), err)
time.Sleep(time.Second)
os.Exit(EXIT_CREATE_COLUMN_MYSQL)
}
return true
} else {
l4g.Critical(utils.T("store.sql.create_column_missing_driver.critical"))
time.Sleep(time.Second)
os.Exit(EXIT_CREATE_COLUMN_MISSING)
return false
}
}
func (ss *SqlSupplier) RemoveColumnIfExists(tableName string, columnName string) bool {
if !ss.DoesColumnExist(tableName, columnName) {
@@ -834,6 +870,10 @@ func (ss *SqlSupplier) Role() store.RoleStore {
return ss.oldStores.role
}
func (ss *SqlSupplier) Scheme() store.SchemeStore {
return ss.oldStores.scheme
}
func (ss *SqlSupplier) DropAllTables() {
ss.master.TruncateTables()
}

View File

@@ -7,6 +7,7 @@ import (
"database/sql"
"net/http"
"strconv"
"strings"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
@@ -20,6 +21,116 @@ type SqlTeamStore struct {
SqlStore
}
type teamMember struct {
TeamId string
UserId string
Roles string
DeleteAt int64
SchemeUser sql.NullBool
SchemeAdmin sql.NullBool
}
func NewTeamMemberFromModel(tm *model.TeamMember) *teamMember {
return &teamMember{
TeamId: tm.TeamId,
UserId: tm.UserId,
Roles: tm.ExplicitRoles,
DeleteAt: tm.DeleteAt,
SchemeUser: sql.NullBool{Valid: true, Bool: tm.SchemeUser},
SchemeAdmin: sql.NullBool{Valid: true, Bool: tm.SchemeAdmin},
}
}
type teamMemberWithSchemeRoles struct {
TeamId string
UserId string
Roles string
DeleteAt int64
SchemeUser sql.NullBool
SchemeAdmin sql.NullBool
TeamSchemeDefaultUserRole sql.NullString
TeamSchemeDefaultAdminRole sql.NullString
}
type teamMemberWithSchemeRolesList []teamMemberWithSchemeRoles
func (db teamMemberWithSchemeRoles) ToModel() *model.TeamMember {
var roles []string
var explicitRoles []string
// Identify any scheme derived roles that are in "Roles" field due to not yet being migrated, and exclude
// them from ExplicitRoles field.
schemeUser := db.SchemeUser.Valid && db.SchemeUser.Bool
schemeAdmin := db.SchemeAdmin.Valid && db.SchemeAdmin.Bool
for _, role := range strings.Fields(db.Roles) {
isImplicit := false
if role == model.TEAM_USER_ROLE_ID {
// We have an implicit role via the system scheme. Override the "schemeUser" field to true.
schemeUser = true
isImplicit = true
} else if role == model.TEAM_ADMIN_ROLE_ID {
// We have an implicit role via the system scheme.
schemeAdmin = true
isImplicit = true
}
if !isImplicit {
explicitRoles = append(explicitRoles, role)
}
roles = append(roles, role)
}
// Add any scheme derived roles that are not in the Roles field due to being Implicit from the Scheme, and add
// them to the Roles field for backwards compatibility reasons.
var schemeImpliedRoles []string
if db.SchemeUser.Valid && db.SchemeUser.Bool {
if db.TeamSchemeDefaultUserRole.Valid && db.TeamSchemeDefaultUserRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultUserRole.String)
} else {
schemeImpliedRoles = append(schemeImpliedRoles, model.TEAM_USER_ROLE_ID)
}
}
if db.SchemeAdmin.Valid && db.SchemeAdmin.Bool {
if db.TeamSchemeDefaultAdminRole.Valid && db.TeamSchemeDefaultAdminRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultAdminRole.String)
} else {
schemeImpliedRoles = append(schemeImpliedRoles, model.TEAM_ADMIN_ROLE_ID)
}
}
for _, impliedRole := range schemeImpliedRoles {
alreadyThere := false
for _, role := range roles {
if role == impliedRole {
alreadyThere = true
}
}
if !alreadyThere {
roles = append(roles, impliedRole)
}
}
tm := &model.TeamMember{
TeamId: db.TeamId,
UserId: db.UserId,
Roles: strings.Join(roles, " "),
DeleteAt: db.DeleteAt,
SchemeUser: schemeUser,
SchemeAdmin: schemeAdmin,
ExplicitRoles: strings.Join(explicitRoles, " "),
}
return tm
}
func (db teamMemberWithSchemeRolesList) ToModel() []*model.TeamMember {
tms := make([]*model.TeamMember, 0)
for _, tm := range db {
tms = append(tms, tm.ToModel())
}
return tms
}
func NewSqlTeamStore(sqlStore SqlStore) store.TeamStore {
s := &SqlTeamStore{sqlStore}
@@ -34,7 +145,7 @@ func NewSqlTeamStore(sqlStore SqlStore) store.TeamStore {
table.ColMap("AllowedDomains").SetMaxSize(500)
table.ColMap("InviteId").SetMaxSize(32)
tablem := db.AddTableWithName(model.TeamMember{}, "TeamMembers").SetKeys(false, "TeamId", "UserId")
tablem := db.AddTableWithName(teamMember{}, "TeamMembers").SetKeys(false, "TeamId", "UserId")
tablem.ColMap("TeamId").SetMaxSize(26)
tablem.ColMap("UserId").SetMaxSize(26)
tablem.ColMap("Roles").SetMaxSize(64)
@@ -326,12 +437,27 @@ func (s SqlTeamStore) AnalyticsTeamCount() store.StoreChannel {
})
}
var TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY = `
SELECT
TeamMembers.*,
TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole,
TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole
FROM
TeamMembers
LEFT JOIN
Teams ON TeamMembers.TeamId = Teams.Id
LEFT JOIN
Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id
`
func (s SqlTeamStore) SaveMember(member *model.TeamMember, maxUsersPerTeam int) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
if result.Err = member.IsValid(); result.Err != nil {
return
}
dbMember := NewTeamMemberFromModel(member)
if maxUsersPerTeam >= 0 {
if count, err := s.GetMaster().SelectInt(
`SELECT
@@ -354,14 +480,23 @@ func (s SqlTeamStore) SaveMember(member *model.TeamMember, maxUsersPerTeam int)
}
}
if err := s.GetMaster().Insert(member); err != nil {
if err := s.GetMaster().Insert(dbMember); err != nil {
if IsUniqueConstraintError(err, []string{"TeamId", "teammembers_pkey", "PRIMARY"}) {
result.Err = model.NewAppError("SqlTeamStore.SaveMember", TEAM_MEMBER_EXISTS_ERROR, nil, "team_id="+member.TeamId+", user_id="+member.UserId+", "+err.Error(), http.StatusBadRequest)
} else {
result.Err = model.NewAppError("SqlTeamStore.SaveMember", "store.sql_team.save_member.save.app_error", nil, "team_id="+member.TeamId+", user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError)
}
} else {
result.Data = member
var retrievedMember teamMemberWithSchemeRoles
if err := s.GetMaster().SelectOne(&retrievedMember, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.TeamId = :TeamId AND TeamMembers.UserId = :UserId", map[string]interface{}{"TeamId": dbMember.TeamId, "UserId": dbMember.UserId}); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlTeamStore.SaveMember", "store.sql_team.get_member.missing.app_error", nil, "team_id="+dbMember.TeamId+"user_id="+dbMember.UserId+","+err.Error(), http.StatusNotFound)
} else {
result.Err = model.NewAppError("SqlTeamStore.SaveMember", "store.sql_team.get_member.app_error", nil, "team_id="+dbMember.TeamId+"user_id="+dbMember.UserId+","+err.Error(), http.StatusInternalServerError)
}
} else {
result.Data = retrievedMember.ToModel()
}
}
})
}
@@ -374,18 +509,27 @@ func (s SqlTeamStore) UpdateMember(member *model.TeamMember) store.StoreChannel
return
}
if _, err := s.GetMaster().Update(member); err != nil {
if _, err := s.GetMaster().Update(NewTeamMemberFromModel(member)); err != nil {
result.Err = model.NewAppError("SqlTeamStore.UpdateMember", "store.sql_team.save_member.save.app_error", nil, err.Error(), http.StatusInternalServerError)
} else {
result.Data = member
var retrievedMember teamMemberWithSchemeRoles
if err := s.GetMaster().SelectOne(&retrievedMember, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.TeamId = :TeamId AND TeamMembers.UserId = :UserId", map[string]interface{}{"TeamId": member.TeamId, "UserId": member.UserId}); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlTeamStore.UpdateMember", "store.sql_team.get_member.missing.app_error", nil, "team_id="+member.TeamId+"user_id="+member.UserId+","+err.Error(), http.StatusNotFound)
} else {
result.Err = model.NewAppError("SqlTeamStore.UpdateMember", "store.sql_team.get_member.app_error", nil, "team_id="+member.TeamId+"user_id="+member.UserId+","+err.Error(), http.StatusInternalServerError)
}
} else {
result.Data = retrievedMember.ToModel()
}
}
})
}
func (s SqlTeamStore) GetMember(teamId string, userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var member model.TeamMember
err := s.GetReplica().SelectOne(&member, "SELECT * FROM TeamMembers WHERE TeamId = :TeamId AND UserId = :UserId", map[string]interface{}{"TeamId": teamId, "UserId": userId})
var dbMember teamMemberWithSchemeRoles
err := s.GetReplica().SelectOne(&dbMember, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.TeamId = :TeamId AND TeamMembers.UserId = :UserId", map[string]interface{}{"TeamId": teamId, "UserId": userId})
if err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlTeamStore.GetMember", "store.sql_team.get_member.missing.app_error", nil, "teamId="+teamId+" userId="+userId+" "+err.Error(), http.StatusNotFound)
@@ -393,19 +537,19 @@ func (s SqlTeamStore) GetMember(teamId string, userId string) store.StoreChannel
result.Err = model.NewAppError("SqlTeamStore.GetMember", "store.sql_team.get_member.app_error", nil, "teamId="+teamId+" userId="+userId+" "+err.Error(), http.StatusInternalServerError)
}
} else {
result.Data = &member
result.Data = dbMember.ToModel()
}
})
}
func (s SqlTeamStore) GetMembers(teamId string, offset int, limit int) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var members []*model.TeamMember
_, err := s.GetReplica().Select(&members, "SELECT * FROM TeamMembers WHERE TeamId = :TeamId AND DeleteAt = 0 LIMIT :Limit OFFSET :Offset", map[string]interface{}{"TeamId": teamId, "Offset": offset, "Limit": limit})
var dbMembers teamMemberWithSchemeRolesList
_, err := s.GetReplica().Select(&dbMembers, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.TeamId = :TeamId AND TeamMembers.DeleteAt = 0 LIMIT :Limit OFFSET :Offset", map[string]interface{}{"TeamId": teamId, "Limit": limit, "Offset": offset})
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetMembers", "store.sql_team.get_members.app_error", nil, "teamId="+teamId+" "+err.Error(), http.StatusInternalServerError)
} else {
result.Data = members
result.Data = dbMembers.ToModel()
}
})
}
@@ -453,7 +597,7 @@ func (s SqlTeamStore) GetActiveMemberCount(teamId string) store.StoreChannel {
func (s SqlTeamStore) GetMembersByIds(teamId string, userIds []string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var members []*model.TeamMember
var dbMembers teamMemberWithSchemeRolesList
props := make(map[string]interface{})
idQuery := ""
@@ -468,22 +612,22 @@ func (s SqlTeamStore) GetMembersByIds(teamId string, userIds []string) store.Sto
props["TeamId"] = teamId
if _, err := s.GetReplica().Select(&members, "SELECT * FROM TeamMembers WHERE TeamId = :TeamId AND UserId IN ("+idQuery+") AND DeleteAt = 0", props); err != nil {
if _, err := s.GetReplica().Select(&dbMembers, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.TeamId = :TeamId AND TeamMembers.UserId IN ("+idQuery+") AND TeamMembers.DeleteAt = 0", props); err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetMembersByIds", "store.sql_team.get_members_by_ids.app_error", nil, "teamId="+teamId+" "+err.Error(), http.StatusInternalServerError)
} else {
result.Data = members
result.Data = dbMembers.ToModel()
}
})
}
func (s SqlTeamStore) GetTeamsForUser(userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var members []*model.TeamMember
_, err := s.GetReplica().Select(&members, "SELECT * FROM TeamMembers WHERE UserId = :UserId", map[string]interface{}{"UserId": userId})
var dbMembers teamMemberWithSchemeRolesList
_, err := s.GetReplica().Select(&dbMembers, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.UserId = :UserId", map[string]interface{}{"UserId": userId})
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetMembers", "store.sql_team.get_members.app_error", nil, "userId="+userId+" "+err.Error(), http.StatusInternalServerError)
} else {
result.Data = members
result.Data = dbMembers.ToModel()
}
})
}
@@ -570,3 +714,15 @@ func (us SqlTeamStore) UpdateLastTeamIconUpdate(teamId string, curTime int64) st
}
})
}
func (s SqlTeamStore) GetTeamsByScheme(schemeId string, offset int, limit int) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var teams []*model.Team
_, err := s.GetReplica().Select(&teams, "SELECT * FROM Teams WHERE SchemeId = :SchemeId ORDER BY DisplayName LIMIT :Limit OFFSET :Offset", map[string]interface{}{"SchemeId": schemeId, "Offset": offset, "Limit": limit})
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetTeamsByScheme", "store.sql_team.get_by_scheme.app_error", nil, "schemeId="+schemeId+" "+err.Error(), http.StatusInternalServerError)
} else {
result.Data = teams
}
})
}

View File

@@ -4,11 +4,378 @@
package sqlstore
import (
"database/sql"
"testing"
"github.com/stretchr/testify/assert"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store/storetest"
)
func TestTeamStore(t *testing.T) {
StoreTest(t, storetest.TestTeamStore)
}
func TestTeamStoreInternalDataTypes(t *testing.T) {
t.Run("NewTeamMemberFromModel", func(t *testing.T) { testNewTeamMemberFromModel(t) })
t.Run("TeamMemberWithSchemeRolesToModel", func(t *testing.T) { testTeamMemberWithSchemeRolesToModel(t) })
}
func testNewTeamMemberFromModel(t *testing.T) {
m := model.TeamMember{
TeamId: model.NewId(),
UserId: model.NewId(),
Roles: "team_user team_admin custom_role",
DeleteAt: 12345,
SchemeUser: true,
SchemeAdmin: true,
ExplicitRoles: "custom_role",
}
db := NewTeamMemberFromModel(&m)
assert.Equal(t, m.TeamId, db.TeamId)
assert.Equal(t, m.UserId, db.UserId)
assert.Equal(t, m.DeleteAt, db.DeleteAt)
assert.Equal(t, true, db.SchemeUser.Valid)
assert.Equal(t, true, db.SchemeAdmin.Valid)
assert.Equal(t, m.SchemeUser, db.SchemeUser.Bool)
assert.Equal(t, m.SchemeAdmin, db.SchemeAdmin.Bool)
assert.Equal(t, m.ExplicitRoles, db.Roles)
}
func testTeamMemberWithSchemeRolesToModel(t *testing.T) {
// Test all the non-role-related properties here.
t.Run("BasicProperties", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
TeamId: model.NewId(),
UserId: model.NewId(),
Roles: "custom_role",
DeleteAt: 12345,
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, db.TeamId, m.TeamId)
assert.Equal(t, db.UserId, m.UserId)
assert.Equal(t, "custom_role team_user team_admin", m.Roles)
assert.Equal(t, db.DeleteAt, m.DeleteAt)
assert.Equal(t, db.SchemeUser.Bool, m.SchemeUser)
assert.Equal(t, db.SchemeAdmin.Bool, m.SchemeAdmin)
assert.Equal(t, db.Roles, m.ExplicitRoles)
})
// Example data *before* the Phase 2 migration has taken place.
t.Run("Unmigrated_NoScheme_User", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "team_user",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "team_user", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_Admin", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "team_user team_admin",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "team_user team_admin", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, true, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_CustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "custom_role", m.Roles)
assert.Equal(t, false, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_UserAndCustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "team_user custom_role",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "team_user custom_role", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_AdminAndCustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "team_user team_admin custom_role",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "team_user team_admin custom_role", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, true, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_NoRoles", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "", m.Roles)
assert.Equal(t, false, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
// Example data *after* the Phase 2 migration has taken place.
t.Run("Migrated_NoScheme_User", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "team_user", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
t.Run("Migrated_NoScheme_Admin", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "team_user team_admin", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, true, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
t.Run("Migrated_NoScheme_CustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "custom_role", m.Roles)
assert.Equal(t, false, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Migrated_NoScheme_UserAndCustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "custom_role team_user", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Migrated_NoScheme_AdminAndCustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "custom_role team_user team_admin", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, true, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Migrated_NoScheme_NoRoles", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "", m.Roles)
assert.Equal(t, false, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
// Example data with a team scheme.
t.Run("Migrated_TeamScheme_User", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_user"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_admin"},
}
m := db.ToModel()
assert.Equal(t, "tscheme_user", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_Admin", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_user"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_admin"},
}
m := db.ToModel()
assert.Equal(t, "tscheme_user tscheme_admin", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, true, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_CustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_user"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_admin"},
}
m := db.ToModel()
assert.Equal(t, "custom_role", m.Roles)
assert.Equal(t, false, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_UserAndCustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_user"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_admin"},
}
m := db.ToModel()
assert.Equal(t, "custom_role tscheme_user", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_AdminAndCustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_user"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_admin"},
}
m := db.ToModel()
assert.Equal(t, "custom_role tscheme_user tscheme_admin", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, true, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_NoRoles", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_user"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_admin"},
}
m := db.ToModel()
assert.Equal(t, "", m.Roles)
assert.Equal(t, false, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
}

View File

@@ -420,5 +420,18 @@ func UpgradeDatabaseToVersion410(sqlStore SqlStore) {
sqlStore.RemoveIndexIfExists("ClientId_2", "OAuthAccessData")
// saveSchemaVersion(sqlStore, VERSION_4_10_0)
sqlStore.CreateColumnIfNotExistsNoDefault("Teams", "SchemeId", "varchar(26)", "varchar(26)")
sqlStore.CreateColumnIfNotExistsNoDefault("Channels", "SchemeId", "varchar(26)", "varchar(26)")
sqlStore.CreateColumnIfNotExistsNoDefault("TeamMembers", "SchemeUser", "boolean", "boolean")
sqlStore.CreateColumnIfNotExistsNoDefault("TeamMembers", "SchemeAdmin", "boolean", "boolean")
sqlStore.CreateColumnIfNotExistsNoDefault("ChannelMembers", "SchemeUser", "boolean", "boolean")
sqlStore.CreateColumnIfNotExistsNoDefault("ChannelMembers", "SchemeAdmin", "boolean", "boolean")
sqlStore.CreateColumnIfNotExists("Roles", "BuiltIn", "boolean", "boolean", "0")
sqlStore.GetMaster().Exec("UPDATE Roles SET BuiltIn=true")
sqlStore.GetMaster().Exec("UPDATE Roles SET SchemeManaged=false WHERE Name NOT IN ('system_user', 'system_admin', 'team_user', 'team_admin', 'channel_user', 'channel_admin')")
// saveSchemaVersion(sqlStore, VERSION_4_9_0)
//}
}

View File

@@ -62,6 +62,7 @@ type Store interface {
FileInfo() FileInfoStore
Reaction() ReactionStore
Role() RoleStore
Scheme() SchemeStore
Job() JobStore
UserAccessToken() UserAccessTokenStore
ChannelMemberHistory() ChannelMemberHistoryStore
@@ -105,6 +106,7 @@ type TeamStore interface {
RemoveAllMembersByTeam(teamId string) StoreChannel
RemoveAllMembersByUser(userId string) StoreChannel
UpdateLastTeamIconUpdate(teamId string, curTime int64) StoreChannel
GetTeamsByScheme(schemeId string, offset int, limit int) StoreChannel
}
type ChannelStore interface {
@@ -162,6 +164,7 @@ type ChannelStore interface {
AnalyticsDeletedTypeCount(teamId string, channelType string) StoreChannel
GetChannelUnread(channelId, userId string) StoreChannel
ClearCaches()
GetChannelsByScheme(schemeId string, offset int, limit int) StoreChannel
}
type ChannelMemberHistoryStore interface {
@@ -477,5 +480,12 @@ type RoleStore interface {
Get(roleId string) StoreChannel
GetByName(name string) StoreChannel
GetByNames(names []string) StoreChannel
Delete(roldId string) StoreChannel
PermanentDeleteAll() StoreChannel
}
type SchemeStore interface {
Save(scheme *model.Scheme) StoreChannel
Get(schemeId string) StoreChannel
Delete(schemeId string) StoreChannel
}

View File

@@ -16,6 +16,8 @@ import (
)
func TestChannelStore(t *testing.T, ss store.Store) {
createDefaultRoles(t, ss)
t.Run("Save", func(t *testing.T) { testChannelStoreSave(t, ss) })
t.Run("SaveDirectChannel", func(t *testing.T) { testChannelStoreSaveDirectChannel(t, ss) })
t.Run("CreateDirectChannel", func(t *testing.T) { testChannelStoreCreateDirectChannel(t, ss) })
@@ -49,6 +51,8 @@ func TestChannelStore(t *testing.T, ss store.Store) {
t.Run("AnalyticsDeletedTypeCount", func(t *testing.T) { testChannelStoreAnalyticsDeletedTypeCount(t, ss) })
t.Run("GetPinnedPosts", func(t *testing.T) { testChannelStoreGetPinnedPosts(t, ss) })
t.Run("MaxChannelsPerTeam", func(t *testing.T) { testChannelStoreMaxChannelsPerTeam(t, ss) })
t.Run("GetChannelsByScheme", func(t *testing.T) { testChannelStoreGetChannelsByScheme(t, ss) })
}
func testChannelStoreSave(t *testing.T, ss store.Store) {
@@ -2186,3 +2190,67 @@ func testChannelStoreMaxChannelsPerTeam(t *testing.T, ss store.Store) {
result = <-ss.Channel().Save(channel, 1)
assert.Nil(t, result.Err)
}
func testChannelStoreGetChannelsByScheme(t *testing.T, ss store.Store) {
// Create some schemes.
s1 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_CHANNEL,
}
s2 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_CHANNEL,
}
s1 = (<-ss.Scheme().Save(s1)).Data.(*model.Scheme)
s2 = (<-ss.Scheme().Save(s2)).Data.(*model.Scheme)
// Create and save some teams.
c1 := &model.Channel{
TeamId: model.NewId(),
DisplayName: "Name",
Name: model.NewId(),
Type: model.CHANNEL_OPEN,
SchemeId: &s1.Id,
}
c2 := &model.Channel{
TeamId: model.NewId(),
DisplayName: "Name",
Name: model.NewId(),
Type: model.CHANNEL_OPEN,
SchemeId: &s1.Id,
}
c3 := &model.Channel{
TeamId: model.NewId(),
DisplayName: "Name",
Name: model.NewId(),
Type: model.CHANNEL_OPEN,
}
c1 = (<-ss.Channel().Save(c1, 100)).Data.(*model.Channel)
c2 = (<-ss.Channel().Save(c2, 100)).Data.(*model.Channel)
c3 = (<-ss.Channel().Save(c3, 100)).Data.(*model.Channel)
// Get the channels by a valid Scheme ID.
res1 := <-ss.Channel().GetChannelsByScheme(s1.Id, 0, 100)
assert.Nil(t, res1.Err)
d1 := res1.Data.([]*model.Channel)
assert.Len(t, d1, 2)
// Get the channels by a valid Scheme ID where there aren't any matching Channel.
res2 := <-ss.Channel().GetChannelsByScheme(s2.Id, 0, 100)
assert.Nil(t, res2.Err)
d2 := res2.Data.([]*model.Channel)
assert.Len(t, d2, 0)
// Get the channels by an invalid Scheme ID.
res3 := <-ss.Channel().GetChannelsByScheme(model.NewId(), 0, 100)
assert.Nil(t, res3.Err)
d3 := res3.Data.([]*model.Channel)
assert.Len(t, d3, 0)
}

View File

@@ -258,6 +258,22 @@ func (_m *ChannelStore) GetChannels(teamId string, userId string) store.StoreCha
return r0
}
// GetChannelsByScheme provides a mock function with given fields: schemeId, offset, limit
func (_m *ChannelStore) GetChannelsByScheme(schemeId string, offset int, limit int) store.StoreChannel {
ret := _m.Called(schemeId, offset, limit)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, int, int) store.StoreChannel); ok {
r0 = rf(schemeId, offset, limit)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// GetDeleted provides a mock function with given fields: team_id, offset, limit
func (_m *ChannelStore) GetDeleted(team_id string, offset int, limit int) store.StoreChannel {
ret := _m.Called(team_id, offset, limit)

View File

@@ -432,6 +432,29 @@ func (_m *LayeredStoreDatabaseLayer) Role() store.RoleStore {
return r0
}
// RoleDelete provides a mock function with given fields: ctx, roldId, hints
func (_m *LayeredStoreDatabaseLayer) RoleDelete(ctx context.Context, roldId 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, roldId)
_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, roldId, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
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))
@@ -547,6 +570,91 @@ func (_m *LayeredStoreDatabaseLayer) RoleSave(ctx context.Context, role *model.R
return r0
}
// Scheme provides a mock function with given fields:
func (_m *LayeredStoreDatabaseLayer) Scheme() store.SchemeStore {
ret := _m.Called()
var r0 store.SchemeStore
if rf, ok := ret.Get(0).(func() store.SchemeStore); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.SchemeStore)
}
}
return r0
}
// SchemeDelete provides a mock function with given fields: ctx, schemeId, hints
func (_m *LayeredStoreDatabaseLayer) SchemeDelete(ctx context.Context, schemeId 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, schemeId)
_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, schemeId, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// SchemeGet provides a mock function with given fields: ctx, schemeId, hints
func (_m *LayeredStoreDatabaseLayer) SchemeGet(ctx context.Context, schemeId 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, schemeId)
_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, schemeId, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// SchemeSave provides a mock function with given fields: ctx, scheme, hints
func (_m *LayeredStoreDatabaseLayer) SchemeSave(ctx context.Context, scheme *model.Scheme, 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, scheme)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, *model.Scheme, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, scheme, 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()

View File

@@ -145,6 +145,29 @@ func (_m *LayeredStoreSupplier) ReactionSave(ctx context.Context, reaction *mode
return r0
}
// RoleDelete provides a mock function with given fields: ctx, roldId, hints
func (_m *LayeredStoreSupplier) RoleDelete(ctx context.Context, roldId 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, roldId)
_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, roldId, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
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))
@@ -260,6 +283,75 @@ func (_m *LayeredStoreSupplier) RoleSave(ctx context.Context, role *model.Role,
return r0
}
// SchemeDelete provides a mock function with given fields: ctx, schemeId, hints
func (_m *LayeredStoreSupplier) SchemeDelete(ctx context.Context, schemeId 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, schemeId)
_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, schemeId, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// SchemeGet provides a mock function with given fields: ctx, schemeId, hints
func (_m *LayeredStoreSupplier) SchemeGet(ctx context.Context, schemeId 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, schemeId)
_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, schemeId, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// SchemeSave provides a mock function with given fields: ctx, scheme, hints
func (_m *LayeredStoreSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, 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, scheme)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, *model.Scheme, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, scheme, 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)

View File

@@ -13,6 +13,22 @@ type RoleStore struct {
mock.Mock
}
// Delete provides a mock function with given fields: roldId
func (_m *RoleStore) Delete(roldId string) store.StoreChannel {
ret := _m.Called(roldId)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok {
r0 = rf(roldId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// Get provides a mock function with given fields: roleId
func (_m *RoleStore) Get(roleId string) store.StoreChannel {
ret := _m.Called(roleId)

View File

@@ -0,0 +1,62 @@
// 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"
// SchemeStore is an autogenerated mock type for the SchemeStore type
type SchemeStore struct {
mock.Mock
}
// Delete provides a mock function with given fields: schemeId
func (_m *SchemeStore) Delete(schemeId string) store.StoreChannel {
ret := _m.Called(schemeId)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok {
r0 = rf(schemeId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// Get provides a mock function with given fields: schemeId
func (_m *SchemeStore) Get(schemeId string) store.StoreChannel {
ret := _m.Called(schemeId)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok {
r0 = rf(schemeId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// Save provides a mock function with given fields: scheme
func (_m *SchemeStore) Save(scheme *model.Scheme) store.StoreChannel {
ret := _m.Called(scheme)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(*model.Scheme) store.StoreChannel); ok {
r0 = rf(scheme)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}

View File

@@ -554,6 +554,22 @@ func (_m *SqlStore) Role() store.RoleStore {
return r0
}
// Scheme provides a mock function with given fields:
func (_m *SqlStore) Scheme() store.SchemeStore {
ret := _m.Called()
var r0 store.SchemeStore
if rf, ok := ret.Get(0).(func() store.SchemeStore); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.SchemeStore)
}
}
return r0
}
// Session provides a mock function with given fields:
func (_m *SqlStore) Session() store.SessionStore {
ret := _m.Called()

View File

@@ -299,6 +299,22 @@ func (_m *Store) Role() store.RoleStore {
return r0
}
// Scheme provides a mock function with given fields:
func (_m *Store) Scheme() store.SchemeStore {
ret := _m.Called()
var r0 store.SchemeStore
if rf, ok := ret.Get(0).(func() store.SchemeStore); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.SchemeStore)
}
}
return r0
}
// Session provides a mock function with given fields:
func (_m *Store) Session() store.SessionStore {
ret := _m.Called()

View File

@@ -237,6 +237,22 @@ func (_m *TeamStore) GetMembersByIds(teamId string, userIds []string) store.Stor
return r0
}
// GetTeamsByScheme provides a mock function with given fields: schemeId, offset, limit
func (_m *TeamStore) GetTeamsByScheme(schemeId string, offset int, limit int) store.StoreChannel {
ret := _m.Called(schemeId, offset, limit)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, int, int) store.StoreChannel); ok {
r0 = rf(schemeId, offset, limit)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// GetTeamsByUserId provides a mock function with given fields: userId
func (_m *TeamStore) GetTeamsByUserId(userId string) store.StoreChannel {
ret := _m.Called(userId)

View File

@@ -17,6 +17,7 @@ func TestRoleStore(t *testing.T, ss store.Store) {
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) })
t.Run("Delete", func(t *testing.T) { testRoleStoreDelete(t, ss) })
t.Run("PermanentDeleteAll", func(t *testing.T) { testRoleStorePermanentDeleteAll(t, ss) })
}
@@ -244,6 +245,49 @@ func testRoleStoreGetByNames(t *testing.T, ss store.Store) {
assert.NotContains(t, roles6, d3)
}
func testRoleStoreDelete(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)
// Check the role is there.
res2 := <-ss.Role().Get(d1.Id)
assert.Nil(t, res2.Err)
// Delete the role.
res3 := <-ss.Role().Delete(d1.Id)
assert.Nil(t, res3.Err)
// Check the role is deleted there.
res4 := <-ss.Role().Get(d1.Id)
assert.Nil(t, res4.Err)
d2 := res4.Data.(*model.Role)
assert.NotZero(t, d2.DeleteAt)
res5 := <-ss.Role().GetByName(d1.Name)
assert.Nil(t, res5.Err)
d3 := res5.Data.(*model.Role)
assert.NotZero(t, d3.DeleteAt)
// Try and delete a role that does not exist.
res6 := <-ss.Role().Delete(model.NewId())
assert.NotNil(t, res6.Err)
}
func testRoleStorePermanentDeleteAll(t *testing.T, ss store.Store) {
r1 := &model.Role{
Name: model.NewId(),
@@ -256,6 +300,7 @@ func testRoleStorePermanentDeleteAll(t *testing.T, ss store.Store) {
},
SchemeManaged: false,
}
r2 := &model.Role{
Name: model.NewId(),
DisplayName: model.NewId(),

View File

@@ -0,0 +1,303 @@
// 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 TestSchemeStore(t *testing.T, ss store.Store) {
createDefaultRoles(t, ss)
t.Run("Save", func(t *testing.T) { testSchemeStoreSave(t, ss) })
t.Run("Get", func(t *testing.T) { testSchemeStoreGet(t, ss) })
t.Run("Delete", func(t *testing.T) { testSchemeStoreDelete(t, ss) })
}
func createDefaultRoles(t *testing.T, ss store.Store) {
<-ss.Role().Save(&model.Role{
Name: model.TEAM_ADMIN_ROLE_ID,
DisplayName: model.TEAM_ADMIN_ROLE_ID,
Permissions: []string{
model.PERMISSION_EDIT_OTHERS_POSTS.Id,
model.PERMISSION_DELETE_OTHERS_POSTS.Id,
},
})
<-ss.Role().Save(&model.Role{
Name: model.TEAM_USER_ROLE_ID,
DisplayName: model.TEAM_USER_ROLE_ID,
Permissions: []string{
model.PERMISSION_VIEW_TEAM.Id,
model.PERMISSION_ADD_USER_TO_TEAM.Id,
},
})
<-ss.Role().Save(&model.Role{
Name: model.CHANNEL_ADMIN_ROLE_ID,
DisplayName: model.CHANNEL_ADMIN_ROLE_ID,
Permissions: []string{
model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id,
},
})
<-ss.Role().Save(&model.Role{
Name: model.CHANNEL_USER_ROLE_ID,
DisplayName: model.CHANNEL_USER_ROLE_ID,
Permissions: []string{
model.PERMISSION_READ_CHANNEL.Id,
model.PERMISSION_CREATE_POST.Id,
},
})
}
func testSchemeStoreSave(t *testing.T, ss store.Store) {
// Save a new scheme.
s1 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_TEAM,
}
// Check all fields saved correctly.
res1 := <-ss.Scheme().Save(s1)
assert.Nil(t, res1.Err)
d1 := res1.Data.(*model.Scheme)
assert.Len(t, d1.Id, 26)
assert.Equal(t, s1.Name, d1.Name)
assert.Equal(t, s1.Description, d1.Description)
assert.NotZero(t, d1.CreateAt)
assert.NotZero(t, d1.UpdateAt)
assert.Zero(t, d1.DeleteAt)
assert.Equal(t, s1.Scope, d1.Scope)
assert.Len(t, d1.DefaultTeamAdminRole, 26)
assert.Len(t, d1.DefaultTeamUserRole, 26)
assert.Len(t, d1.DefaultChannelAdminRole, 26)
assert.Len(t, d1.DefaultChannelUserRole, 26)
// Check the default roles were created correctly.
roleRes1 := <-ss.Role().Get(d1.DefaultTeamAdminRole)
assert.Nil(t, roleRes1.Err)
role1 := roleRes1.Data.(*model.Role)
assert.Equal(t, role1.Permissions, []string{"edit_others_posts", "delete_others_posts"})
assert.True(t, role1.SchemeManaged)
roleRes2 := <-ss.Role().Get(d1.DefaultTeamUserRole)
assert.Nil(t, roleRes2.Err)
role2 := roleRes2.Data.(*model.Role)
assert.Equal(t, role2.Permissions, []string{"view_team", "add_user_to_team"})
assert.True(t, role2.SchemeManaged)
roleRes3 := <-ss.Role().Get(d1.DefaultChannelAdminRole)
assert.Nil(t, roleRes3.Err)
role3 := roleRes3.Data.(*model.Role)
assert.Equal(t, role3.Permissions, []string{"manage_public_channel_members", "manage_private_channel_members"})
assert.True(t, role3.SchemeManaged)
roleRes4 := <-ss.Role().Get(d1.DefaultChannelUserRole)
assert.Nil(t, roleRes4.Err)
role4 := roleRes4.Data.(*model.Role)
assert.Equal(t, role4.Permissions, []string{"read_channel", "create_post"})
assert.True(t, role4.SchemeManaged)
// Change the scheme description and update.
d1.Description = model.NewId()
res2 := <-ss.Scheme().Save(d1)
assert.Nil(t, res2.Err)
d2 := res2.Data.(*model.Scheme)
assert.Equal(t, d1.Id, d2.Id)
assert.Equal(t, s1.Name, d2.Name)
assert.Equal(t, d1.Description, d2.Description)
assert.NotZero(t, d2.CreateAt)
assert.NotZero(t, d2.UpdateAt)
assert.Zero(t, d2.DeleteAt)
assert.Equal(t, s1.Scope, d2.Scope)
assert.Equal(t, d1.DefaultTeamAdminRole, d2.DefaultTeamAdminRole)
assert.Equal(t, d1.DefaultTeamUserRole, d2.DefaultTeamUserRole)
assert.Equal(t, d1.DefaultChannelAdminRole, d2.DefaultChannelAdminRole)
assert.Equal(t, d1.DefaultChannelUserRole, d2.DefaultChannelUserRole)
// Try saving one with an invalid ID set.
s3 := &model.Scheme{
Id: model.NewId(),
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_TEAM,
}
res3 := <-ss.Scheme().Save(s3)
assert.NotNil(t, res3.Err)
}
func testSchemeStoreGet(t *testing.T, ss store.Store) {
// Save a scheme to test with.
s1 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_TEAM,
}
res1 := <-ss.Scheme().Save(s1)
assert.Nil(t, res1.Err)
d1 := res1.Data.(*model.Scheme)
assert.Len(t, d1.Id, 26)
// Get a valid scheme
res2 := <-ss.Scheme().Get(d1.Id)
assert.Nil(t, res2.Err)
d2 := res1.Data.(*model.Scheme)
assert.Equal(t, d1.Id, d2.Id)
assert.Equal(t, s1.Name, d2.Name)
assert.Equal(t, d1.Description, d2.Description)
assert.NotZero(t, d2.CreateAt)
assert.NotZero(t, d2.UpdateAt)
assert.Zero(t, d2.DeleteAt)
assert.Equal(t, s1.Scope, d2.Scope)
assert.Equal(t, d1.DefaultTeamAdminRole, d2.DefaultTeamAdminRole)
assert.Equal(t, d1.DefaultTeamUserRole, d2.DefaultTeamUserRole)
assert.Equal(t, d1.DefaultChannelAdminRole, d2.DefaultChannelAdminRole)
assert.Equal(t, d1.DefaultChannelUserRole, d2.DefaultChannelUserRole)
// Get an invalid scheme
res3 := <-ss.Scheme().Get(model.NewId())
assert.NotNil(t, res3.Err)
}
func testSchemeStoreDelete(t *testing.T, ss store.Store) {
// Save a new scheme.
s1 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_TEAM,
}
// Check all fields saved correctly.
res1 := <-ss.Scheme().Save(s1)
assert.Nil(t, res1.Err)
d1 := res1.Data.(*model.Scheme)
assert.Len(t, d1.Id, 26)
assert.Equal(t, s1.Name, d1.Name)
assert.Equal(t, s1.Description, d1.Description)
assert.NotZero(t, d1.CreateAt)
assert.NotZero(t, d1.UpdateAt)
assert.Zero(t, d1.DeleteAt)
assert.Equal(t, s1.Scope, d1.Scope)
assert.Len(t, d1.DefaultTeamAdminRole, 26)
assert.Len(t, d1.DefaultTeamUserRole, 26)
assert.Len(t, d1.DefaultChannelAdminRole, 26)
assert.Len(t, d1.DefaultChannelUserRole, 26)
// Check the default roles were created correctly.
roleRes1 := <-ss.Role().Get(d1.DefaultTeamAdminRole)
assert.Nil(t, roleRes1.Err)
role1 := roleRes1.Data.(*model.Role)
assert.Equal(t, role1.Permissions, []string{"edit_others_posts", "delete_others_posts"})
assert.True(t, role1.SchemeManaged)
roleRes2 := <-ss.Role().Get(d1.DefaultTeamUserRole)
assert.Nil(t, roleRes2.Err)
role2 := roleRes2.Data.(*model.Role)
assert.Equal(t, role2.Permissions, []string{"view_team", "add_user_to_team"})
assert.True(t, role2.SchemeManaged)
roleRes3 := <-ss.Role().Get(d1.DefaultChannelAdminRole)
assert.Nil(t, roleRes3.Err)
role3 := roleRes3.Data.(*model.Role)
assert.Equal(t, role3.Permissions, []string{"manage_public_channel_members", "manage_private_channel_members"})
assert.True(t, role3.SchemeManaged)
roleRes4 := <-ss.Role().Get(d1.DefaultChannelUserRole)
assert.Nil(t, roleRes4.Err)
role4 := roleRes4.Data.(*model.Role)
assert.Equal(t, role4.Permissions, []string{"read_channel", "create_post"})
assert.True(t, role4.SchemeManaged)
// Delete the scheme.
res2 := <-ss.Scheme().Delete(d1.Id)
if !assert.Nil(t, res2.Err) {
t.Fatal(res2.Err)
}
d2 := res2.Data.(*model.Scheme)
assert.NotZero(t, d2.DeleteAt)
// Check that the roles are deleted too.
roleRes5 := <-ss.Role().Get(d1.DefaultTeamAdminRole)
assert.Nil(t, roleRes5.Err)
role5 := roleRes5.Data.(*model.Role)
assert.NotZero(t, role5.DeleteAt)
roleRes6 := <-ss.Role().Get(d1.DefaultTeamUserRole)
assert.Nil(t, roleRes6.Err)
role6 := roleRes6.Data.(*model.Role)
assert.NotZero(t, role6.DeleteAt)
roleRes7 := <-ss.Role().Get(d1.DefaultChannelAdminRole)
assert.Nil(t, roleRes7.Err)
role7 := roleRes7.Data.(*model.Role)
assert.NotZero(t, role7.DeleteAt)
roleRes8 := <-ss.Role().Get(d1.DefaultChannelUserRole)
assert.Nil(t, roleRes8.Err)
role8 := roleRes8.Data.(*model.Role)
assert.NotZero(t, role8.DeleteAt)
// Try deleting a scheme that does not exist.
res3 := <-ss.Scheme().Delete(model.NewId())
assert.NotNil(t, res3.Err)
// Try deleting a team scheme that's in use.
s4 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_TEAM,
}
res4 := <-ss.Scheme().Save(s4)
assert.Nil(t, res4.Err)
d4 := res4.Data.(*model.Scheme)
t4 := &model.Team{
Name: model.NewId(),
DisplayName: model.NewId(),
Email: model.NewId() + "@nowhere.com",
Type: model.TEAM_OPEN,
SchemeId: &d4.Id,
}
tres4 := <-ss.Team().Save(t4)
assert.Nil(t, tres4.Err)
t4 = tres4.Data.(*model.Team)
sres4 := <-ss.Scheme().Delete(d4.Id)
assert.NotNil(t, sres4.Err)
// Try deleting a channel scheme that's in use.
s5 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_CHANNEL,
}
res5 := <-ss.Scheme().Save(s5)
assert.Nil(t, res5.Err)
d5 := res5.Data.(*model.Scheme)
c5 := &model.Channel{
TeamId: model.NewId(),
DisplayName: model.NewId(),
Name: model.NewId(),
Type: model.CHANNEL_OPEN,
SchemeId: &d5.Id,
}
cres5 := <-ss.Channel().Save(c5, -1)
assert.Nil(t, cres5.Err)
c5 = cres5.Data.(*model.Channel)
sres5 := <-ss.Scheme().Delete(d5.Id)
assert.NotNil(t, sres5.Err)
}

View File

@@ -44,6 +44,7 @@ type Store struct {
PluginStore mocks.PluginStore
ChannelMemberHistoryStore mocks.ChannelMemberHistoryStore
RoleStore mocks.RoleStore
SchemeStore mocks.SchemeStore
}
func (s *Store) Team() store.TeamStore { return &s.TeamStore }
@@ -70,6 +71,7 @@ func (s *Store) Job() store.JobStore { return &s.JobSt
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) Scheme() store.SchemeStore { return &s.SchemeStore }
func (s *Store) ChannelMemberHistory() store.ChannelMemberHistoryStore {
return &s.ChannelMemberHistoryStore
}
@@ -107,5 +109,6 @@ func (s *Store) AssertExpectations(t mock.TestingT) bool {
&s.ChannelMemberHistoryStore,
&s.PluginStore,
&s.RoleStore,
&s.SchemeStore,
)
}

View File

@@ -7,11 +7,15 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
func TestTeamStore(t *testing.T, ss store.Store) {
createDefaultRoles(t, ss)
t.Run("Save", func(t *testing.T) { testTeamStoreSave(t, ss) })
t.Run("Update", func(t *testing.T) { testTeamStoreUpdate(t, ss) })
t.Run("UpdateDisplayName", func(t *testing.T) { testTeamStoreUpdateDisplayName(t, ss) })
@@ -34,6 +38,7 @@ func TestTeamStore(t *testing.T, ss store.Store) {
t.Run("GetChannelUnreadsForAllTeams", func(t *testing.T) { testGetChannelUnreadsForAllTeams(t, ss) })
t.Run("GetChannelUnreadsForTeam", func(t *testing.T) { testGetChannelUnreadsForTeam(t, ss) })
t.Run("UpdateLastTeamIconUpdate", func(t *testing.T) { testUpdateLastTeamIconUpdate(t, ss) })
t.Run("GetTeamsByScheme", func(t *testing.T) { testGetTeamsByScheme(t, ss) })
}
func testTeamStoreSave(t *testing.T, ss store.Store) {
@@ -1029,3 +1034,67 @@ func testUpdateLastTeamIconUpdate(t *testing.T, ss store.Store) {
t.Fatal("LastTeamIconUpdate not updated")
}
}
func testGetTeamsByScheme(t *testing.T, ss store.Store) {
// Create some schemes.
s1 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_TEAM,
}
s2 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_TEAM,
}
s1 = (<-ss.Scheme().Save(s1)).Data.(*model.Scheme)
s2 = (<-ss.Scheme().Save(s2)).Data.(*model.Scheme)
// Create and save some teams.
t1 := &model.Team{
Name: model.NewId(),
DisplayName: model.NewId(),
Email: model.NewId() + "@nowhere.com",
Type: model.TEAM_OPEN,
SchemeId: &s1.Id,
}
t2 := &model.Team{
Name: model.NewId(),
DisplayName: model.NewId(),
Email: model.NewId() + "@nowhere.com",
Type: model.TEAM_OPEN,
SchemeId: &s1.Id,
}
t3 := &model.Team{
Name: model.NewId(),
DisplayName: model.NewId(),
Email: model.NewId() + "@nowhere.com",
Type: model.TEAM_OPEN,
}
t1 = (<-ss.Team().Save(t1)).Data.(*model.Team)
t2 = (<-ss.Team().Save(t2)).Data.(*model.Team)
t3 = (<-ss.Team().Save(t3)).Data.(*model.Team)
// Get the teams by a valid Scheme ID.
res1 := <-ss.Team().GetTeamsByScheme(s1.Id, 0, 100)
assert.Nil(t, res1.Err)
d1 := res1.Data.([]*model.Team)
assert.Len(t, d1, 2)
// Get the teams by a valid Scheme ID where there aren't any matching Teams.
res2 := <-ss.Team().GetTeamsByScheme(s2.Id, 0, 100)
assert.Nil(t, res2.Err)
d2 := res2.Data.([]*model.Team)
assert.Len(t, d2, 0)
// Get the teams by an invalid Scheme ID.
res3 := <-ss.Team().GetTeamsByScheme(model.NewId(), 0, 100)
assert.Nil(t, res3.Err)
d3 := res3.Data.([]*model.Team)
assert.Len(t, d3, 0)
}