[MM-60253] Avoid unnecessary cache clearing during LDAP sync (#28300)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
da6b40665a
Коммит
f3eee28f56
@@ -1298,7 +1298,7 @@ func (a *App) UpdateChannelMemberNotifyProps(c request.CTX, data map[string]stri
|
|||||||
a.invalidateCacheForChannelMembersNotifyProps(member.ChannelId)
|
a.invalidateCacheForChannelMembersNotifyProps(member.ChannelId)
|
||||||
|
|
||||||
// Notify the clients that the member notify props changed
|
// Notify the clients that the member notify props changed
|
||||||
err = a.sendUpdateChannelMemberNotifyPropsEvent(member)
|
err = a.sendUpdateChannelMemberEvent(member)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, model.NewAppError("UpdateChannelMemberNotifyProps", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
return nil, model.NewAppError("UpdateChannelMemberNotifyProps", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||||
}
|
}
|
||||||
@@ -1339,7 +1339,7 @@ func (a *App) PatchChannelMembersNotifyProps(c request.CTX, members []*model.Cha
|
|||||||
|
|
||||||
// Notify clients that their notify props have changed
|
// Notify clients that their notify props have changed
|
||||||
for _, member := range updated {
|
for _, member := range updated {
|
||||||
err := a.sendUpdateChannelMemberNotifyPropsEvent(member)
|
err := a.sendUpdateChannelMemberEvent(member)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logger().Warn("Failed to send WebSocket event for updated channel member notify props", mlog.Err(err))
|
c.Logger().Warn("Failed to send WebSocket event for updated channel member notify props", mlog.Err(err))
|
||||||
}
|
}
|
||||||
@@ -1348,7 +1348,7 @@ func (a *App) PatchChannelMembersNotifyProps(c request.CTX, members []*model.Cha
|
|||||||
return updated, nil
|
return updated, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) sendUpdateChannelMemberNotifyPropsEvent(member *model.ChannelMember) error {
|
func (a *App) sendUpdateChannelMemberEvent(member *model.ChannelMember) error {
|
||||||
evt := model.NewWebSocketEvent(model.WebsocketEventChannelMemberUpdated, "", "", member.UserId, nil, "")
|
evt := model.NewWebSocketEvent(model.WebsocketEventChannelMemberUpdated, "", "", member.UserId, nil, "")
|
||||||
memberJSON, jsonErr := json.Marshal(member)
|
memberJSON, jsonErr := json.Marshal(member)
|
||||||
if jsonErr != nil {
|
if jsonErr != nil {
|
||||||
@@ -1962,9 +1962,11 @@ func (a *App) GetAllChannels(c request.CTX, page, perPage int, opts model.Channe
|
|||||||
opts.ExcludeChannelNames = a.DefaultChannelNames(c)
|
opts.ExcludeChannelNames = a.DefaultChannelNames(c)
|
||||||
}
|
}
|
||||||
storeOpts := store.ChannelSearchOpts{
|
storeOpts := store.ChannelSearchOpts{
|
||||||
ExcludeChannelNames: opts.ExcludeChannelNames,
|
|
||||||
NotAssociatedToGroup: opts.NotAssociatedToGroup,
|
NotAssociatedToGroup: opts.NotAssociatedToGroup,
|
||||||
IncludeDeleted: opts.IncludeDeleted,
|
IncludeDeleted: opts.IncludeDeleted,
|
||||||
|
ExcludeChannelNames: opts.ExcludeChannelNames,
|
||||||
|
GroupConstrained: opts.GroupConstrained,
|
||||||
|
ExcludeGroupConstrained: opts.ExcludeGroupConstrained,
|
||||||
ExcludePolicyConstrained: opts.ExcludePolicyConstrained,
|
ExcludePolicyConstrained: opts.ExcludePolicyConstrained,
|
||||||
IncludePolicyID: opts.IncludePolicyID,
|
IncludePolicyID: opts.IncludePolicyID,
|
||||||
}
|
}
|
||||||
@@ -1981,9 +1983,13 @@ func (a *App) GetAllChannelsCount(c request.CTX, opts model.ChannelSearchOpts) (
|
|||||||
opts.ExcludeChannelNames = a.DefaultChannelNames(c)
|
opts.ExcludeChannelNames = a.DefaultChannelNames(c)
|
||||||
}
|
}
|
||||||
storeOpts := store.ChannelSearchOpts{
|
storeOpts := store.ChannelSearchOpts{
|
||||||
ExcludeChannelNames: opts.ExcludeChannelNames,
|
NotAssociatedToGroup: opts.NotAssociatedToGroup,
|
||||||
NotAssociatedToGroup: opts.NotAssociatedToGroup,
|
IncludeDeleted: opts.IncludeDeleted,
|
||||||
IncludeDeleted: opts.IncludeDeleted,
|
ExcludeChannelNames: opts.ExcludeChannelNames,
|
||||||
|
GroupConstrained: opts.GroupConstrained,
|
||||||
|
ExcludeGroupConstrained: opts.ExcludeGroupConstrained,
|
||||||
|
ExcludePolicyConstrained: opts.ExcludePolicyConstrained,
|
||||||
|
IncludePolicyID: opts.IncludePolicyID,
|
||||||
}
|
}
|
||||||
count, err := a.Srv().Store().Channel().GetAllChannelsCount(storeOpts)
|
count, err := a.Srv().Store().Channel().GetAllChannelsCount(storeOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -234,26 +234,47 @@ func (a *App) SyncSyncableRoles(rctx request.CTX, syncableID string, syncableTyp
|
|||||||
|
|
||||||
switch syncableType {
|
switch syncableType {
|
||||||
case model.GroupSyncableTypeTeam:
|
case model.GroupSyncableTypeTeam:
|
||||||
nErr := a.Srv().Store().Team().UpdateMembersRole(syncableID, permittedAdmins)
|
var updatedMembers []*model.TeamMember
|
||||||
if nErr != nil {
|
updatedMembers, err = a.Srv().Store().Team().UpdateMembersRole(syncableID, permittedAdmins)
|
||||||
return model.NewAppError("App.SyncSyncableRoles", "app.update_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
if err != nil {
|
||||||
|
return model.NewAppError("App.SyncSyncableRoles", "app.update_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, member := range updatedMembers {
|
||||||
|
a.ClearSessionCacheForUser(member.UserId)
|
||||||
|
|
||||||
|
if appErr := a.sendUpdatedTeamMemberEvent(member); appErr != nil {
|
||||||
|
rctx.Logger().Warn("Error sending channel member updated websocket event", mlog.Err(appErr))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
case model.GroupSyncableTypeChannel:
|
case model.GroupSyncableTypeChannel:
|
||||||
nErr := a.Srv().Store().Channel().UpdateMembersRole(syncableID, permittedAdmins)
|
var updatedMembers []*model.ChannelMember
|
||||||
if nErr != nil {
|
updatedMembers, err = a.Srv().Store().Channel().UpdateMembersRole(syncableID, permittedAdmins)
|
||||||
return model.NewAppError("App.SyncSyncableRoles", "app.update_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
if err != nil {
|
||||||
|
return model.NewAppError("App.SyncSyncableRoles", "app.update_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, member := range updatedMembers {
|
||||||
|
a.ClearSessionCacheForUser(member.UserId)
|
||||||
|
|
||||||
|
if appErr := a.sendUpdateChannelMemberEvent(member); appErr != nil {
|
||||||
|
rctx.Logger().Warn("Error sending channel member updated websocket event", mlog.Err(appErr))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
default:
|
default:
|
||||||
return model.NewAppError("App.SyncSyncableRoles", "groups.unsupported_syncable_type", map[string]any{"Value": syncableType}, "", http.StatusInternalServerError)
|
return model.NewAppError("App.SyncSyncableRoles", "groups.unsupported_syncable_type", map[string]any{"Value": syncableType}, "", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SyncRolesAndMembership updates the SchemeAdmin status and membership of all of the members of the given
|
// SyncRolesAndMembership updates the SchemeAdmin status and membership of all of the members of the given
|
||||||
// syncable.
|
// syncable.
|
||||||
func (a *App) SyncRolesAndMembership(rctx request.CTX, syncableID string, syncableType model.GroupSyncableType, includeRemovedMembers bool) {
|
func (a *App) SyncRolesAndMembership(rctx request.CTX, syncableID string, syncableType model.GroupSyncableType, includeRemovedMembers bool) {
|
||||||
a.SyncSyncableRoles(rctx, syncableID, syncableType)
|
appErr := a.SyncSyncableRoles(rctx, syncableID, syncableType)
|
||||||
|
if appErr != nil {
|
||||||
|
rctx.Logger().Warn("Error syncing syncable roles", mlog.Err(appErr))
|
||||||
|
}
|
||||||
|
|
||||||
lastJob, _ := a.Srv().Store().Job().GetNewestJobByStatusAndType(model.JobStatusSuccess, model.JobTypeLdapSync)
|
lastJob, _ := a.Srv().Store().Job().GetNewestJobByStatusAndType(model.JobStatusSuccess, model.JobTypeLdapSync)
|
||||||
var since int64
|
var since int64
|
||||||
@@ -272,9 +293,6 @@ func (a *App) SyncRolesAndMembership(rctx request.CTX, syncableID string, syncab
|
|||||||
if err := a.deleteGroupConstrainedTeamMemberships(rctx, &syncableID); err != nil {
|
if err := a.deleteGroupConstrainedTeamMemberships(rctx, &syncableID); err != nil {
|
||||||
rctx.Logger().Warn("Error deleting group constrained team memberships", mlog.Err(err))
|
rctx.Logger().Warn("Error deleting group constrained team memberships", mlog.Err(err))
|
||||||
}
|
}
|
||||||
if err := a.ClearTeamMembersCache(syncableID); err != nil {
|
|
||||||
rctx.Logger().Warn("Error clearing team members cache", mlog.Err(err))
|
|
||||||
}
|
|
||||||
case model.GroupSyncableTypeChannel:
|
case model.GroupSyncableTypeChannel:
|
||||||
params.ScopedChannelID = &syncableID
|
params.ScopedChannelID = &syncableID
|
||||||
if err := a.createDefaultChannelMemberships(rctx, params); err != nil {
|
if err := a.createDefaultChannelMemberships(rctx, params); err != nil {
|
||||||
@@ -283,8 +301,5 @@ func (a *App) SyncRolesAndMembership(rctx request.CTX, syncableID string, syncab
|
|||||||
if err := a.deleteGroupConstrainedChannelMemberships(rctx, &syncableID); err != nil {
|
if err := a.deleteGroupConstrainedChannelMemberships(rctx, &syncableID); err != nil {
|
||||||
rctx.Logger().Warn("Error deleting group constrained team memberships", mlog.Err(err))
|
rctx.Logger().Warn("Error deleting group constrained team memberships", mlog.Err(err))
|
||||||
}
|
}
|
||||||
if err := a.ClearChannelMembersCache(rctx, syncableID); err != nil {
|
|
||||||
rctx.Logger().Warn("Error clearing channel members cache", mlog.Err(err))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -469,7 +469,7 @@ func (a *App) UpdateTeamMemberRoles(c request.CTX, teamID string, userID string,
|
|||||||
|
|
||||||
a.ClearSessionCacheForUser(userID)
|
a.ClearSessionCacheForUser(userID)
|
||||||
|
|
||||||
if appErr := a.sendUpdatedMemberRoleEvent(userID, member); appErr != nil {
|
if appErr := a.sendUpdatedTeamMemberEvent(member); appErr != nil {
|
||||||
return nil, appErr
|
return nil, appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,15 +512,15 @@ func (a *App) UpdateTeamMemberSchemeRoles(c request.CTX, teamID string, userID s
|
|||||||
|
|
||||||
a.ClearSessionCacheForUser(userID)
|
a.ClearSessionCacheForUser(userID)
|
||||||
|
|
||||||
if appErr := a.sendUpdatedMemberRoleEvent(userID, member); appErr != nil {
|
if appErr := a.sendUpdatedTeamMemberEvent(member); appErr != nil {
|
||||||
return nil, appErr
|
return nil, appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
return member, nil
|
return member, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) sendUpdatedMemberRoleEvent(userID string, member *model.TeamMember) *model.AppError {
|
func (a *App) sendUpdatedTeamMemberEvent(member *model.TeamMember) *model.AppError {
|
||||||
message := model.NewWebSocketEvent(model.WebsocketEventMemberroleUpdated, "", "", userID, nil, "")
|
message := model.NewWebSocketEvent(model.WebsocketEventMemberroleUpdated, "", "", member.UserId, nil, "")
|
||||||
tmJSON, jsonErr := json.Marshal(member)
|
tmJSON, jsonErr := json.Marshal(member)
|
||||||
if jsonErr != nil {
|
if jsonErr != nil {
|
||||||
return model.NewAppError("sendUpdatedMemberRoleEvent", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr)
|
return model.NewAppError("sendUpdatedMemberRoleEvent", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr)
|
||||||
|
|||||||
@@ -2443,7 +2443,7 @@ func (a *App) PromoteGuestToUser(c request.CTX, user *model.User, requestorId st
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, member := range teamMembers {
|
for _, member := range teamMembers {
|
||||||
a.sendUpdatedMemberRoleEvent(user.Id, member)
|
a.sendUpdatedTeamMemberEvent(member)
|
||||||
|
|
||||||
channelMembers, appErr := a.GetChannelMembersForUser(c, member.TeamId, user.Id)
|
channelMembers, appErr := a.GetChannelMembersForUser(c, member.TeamId, user.Id)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
@@ -2487,7 +2487,7 @@ func (a *App) DemoteUserToGuest(c request.CTX, user *model.User) *model.AppError
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, member := range teamMembers {
|
for _, member := range teamMembers {
|
||||||
a.sendUpdatedMemberRoleEvent(user.Id, member)
|
a.sendUpdatedTeamMemberEvent(member)
|
||||||
|
|
||||||
channelMembers, appErr := a.GetChannelMembersForUser(c, member.TeamId, user.Id)
|
channelMembers, appErr := a.GetChannelMembersForUser(c, member.TeamId, user.Id)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
|
|||||||
@@ -2563,7 +2563,7 @@ func (s *OpenTracingLayerChannelStore) UpdateMemberNotifyProps(channelID string,
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OpenTracingLayerChannelStore) UpdateMembersRole(channelID string, userIDs []string) error {
|
func (s *OpenTracingLayerChannelStore) UpdateMembersRole(channelID string, userIDs []string) ([]*model.ChannelMember, error) {
|
||||||
origCtx := s.Root.Store.Context()
|
origCtx := s.Root.Store.Context()
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.UpdateMembersRole")
|
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.UpdateMembersRole")
|
||||||
s.Root.Store.SetContext(newCtx)
|
s.Root.Store.SetContext(newCtx)
|
||||||
@@ -2572,13 +2572,13 @@ func (s *OpenTracingLayerChannelStore) UpdateMembersRole(channelID string, userI
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
err := s.ChannelStore.UpdateMembersRole(channelID, userIDs)
|
result, err := s.ChannelStore.UpdateMembersRole(channelID, userIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
span.LogFields(spanlog.Error(err))
|
span.LogFields(spanlog.Error(err))
|
||||||
ext.Error.Set(span, true)
|
ext.Error.Set(span, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OpenTracingLayerChannelStore) UpdateMultipleMembers(members []*model.ChannelMember) ([]*model.ChannelMember, error) {
|
func (s *OpenTracingLayerChannelStore) UpdateMultipleMembers(members []*model.ChannelMember) ([]*model.ChannelMember, error) {
|
||||||
@@ -10554,7 +10554,7 @@ func (s *OpenTracingLayerTeamStore) UpdateMember(rctx request.CTX, member *model
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OpenTracingLayerTeamStore) UpdateMembersRole(teamID string, userIDs []string) error {
|
func (s *OpenTracingLayerTeamStore) UpdateMembersRole(teamID string, adminIDs []string) ([]*model.TeamMember, error) {
|
||||||
origCtx := s.Root.Store.Context()
|
origCtx := s.Root.Store.Context()
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "TeamStore.UpdateMembersRole")
|
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "TeamStore.UpdateMembersRole")
|
||||||
s.Root.Store.SetContext(newCtx)
|
s.Root.Store.SetContext(newCtx)
|
||||||
@@ -10563,13 +10563,13 @@ func (s *OpenTracingLayerTeamStore) UpdateMembersRole(teamID string, userIDs []s
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
err := s.TeamStore.UpdateMembersRole(teamID, userIDs)
|
result, err := s.TeamStore.UpdateMembersRole(teamID, adminIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
span.LogFields(spanlog.Error(err))
|
span.LogFields(spanlog.Error(err))
|
||||||
ext.Error.Set(span, true)
|
ext.Error.Set(span, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OpenTracingLayerTeamStore) UpdateMultipleMembers(members []*model.TeamMember) ([]*model.TeamMember, error) {
|
func (s *OpenTracingLayerTeamStore) UpdateMultipleMembers(members []*model.TeamMember) ([]*model.TeamMember, error) {
|
||||||
|
|||||||
@@ -2840,21 +2840,21 @@ func (s *RetryLayerChannelStore) UpdateMemberNotifyProps(channelID string, userI
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RetryLayerChannelStore) UpdateMembersRole(channelID string, userIDs []string) error {
|
func (s *RetryLayerChannelStore) UpdateMembersRole(channelID string, userIDs []string) ([]*model.ChannelMember, error) {
|
||||||
|
|
||||||
tries := 0
|
tries := 0
|
||||||
for {
|
for {
|
||||||
err := s.ChannelStore.UpdateMembersRole(channelID, userIDs)
|
result, err := s.ChannelStore.UpdateMembersRole(channelID, userIDs)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return result, nil
|
||||||
}
|
}
|
||||||
if !isRepeatableError(err) {
|
if !isRepeatableError(err) {
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
tries++
|
tries++
|
||||||
if tries >= 3 {
|
if tries >= 3 {
|
||||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||||
}
|
}
|
||||||
@@ -12071,21 +12071,21 @@ func (s *RetryLayerTeamStore) UpdateMember(rctx request.CTX, member *model.TeamM
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RetryLayerTeamStore) UpdateMembersRole(teamID string, userIDs []string) error {
|
func (s *RetryLayerTeamStore) UpdateMembersRole(teamID string, adminIDs []string) ([]*model.TeamMember, error) {
|
||||||
|
|
||||||
tries := 0
|
tries := 0
|
||||||
for {
|
for {
|
||||||
err := s.TeamStore.UpdateMembersRole(teamID, userIDs)
|
result, err := s.TeamStore.UpdateMembersRole(teamID, adminIDs)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return result, nil
|
||||||
}
|
}
|
||||||
if !isRepeatableError(err) {
|
if !isRepeatableError(err) {
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
tries++
|
tries++
|
||||||
if tries >= 3 {
|
if tries >= 3 {
|
||||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -1190,6 +1191,15 @@ func (s SqlChannelStore) getAllChannelsQuery(opts store.ChannelSearchOpts, forCo
|
|||||||
query = query.Where("c.Id NOT IN (SELECT ChannelId FROM GroupChannels WHERE GroupChannels.GroupId = ? AND GroupChannels.DeleteAt = 0)", opts.NotAssociatedToGroup)
|
query = query.Where("c.Id NOT IN (SELECT ChannelId FROM GroupChannels WHERE GroupChannels.GroupId = ? AND GroupChannels.DeleteAt = 0)", opts.NotAssociatedToGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.GroupConstrained {
|
||||||
|
query = query.Where(sq.Eq{"c.GroupConstrained": true})
|
||||||
|
} else if opts.ExcludeGroupConstrained {
|
||||||
|
query = query.Where(sq.Or{
|
||||||
|
sq.NotEq{"c.GroupConstrained": true},
|
||||||
|
sq.Eq{"c.GroupConstrained": nil},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if len(opts.ExcludeChannelNames) > 0 {
|
if len(opts.ExcludeChannelNames) > 0 {
|
||||||
query = query.Where(sq.NotEq{"c.Name": opts.ExcludeChannelNames})
|
query = query.Where(sq.NotEq{"c.Name": opts.ExcludeChannelNames})
|
||||||
}
|
}
|
||||||
@@ -4161,27 +4171,76 @@ func (s SqlChannelStore) UserBelongsToChannels(userId string, channelIds []strin
|
|||||||
return c > 0, nil
|
return c > 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: parameterize userIDs
|
// UpdateMembersRole updates all the members of channelID in the adminIDs string array to be admins and sets all other
|
||||||
func (s SqlChannelStore) UpdateMembersRole(channelID string, userIDs []string) error {
|
// users as not being admin.
|
||||||
sql := fmt.Sprintf(`
|
// It returns the list of userIDs whose roles got updated.
|
||||||
UPDATE
|
//
|
||||||
ChannelMembers
|
// TODO: parameterize adminIDs
|
||||||
SET
|
func (s SqlChannelStore) UpdateMembersRole(channelID string, adminIDs []string) (_ []*model.ChannelMember, err error) {
|
||||||
SchemeAdmin = CASE WHEN UserId IN ('%s') THEN
|
transaction, err := s.GetMasterX().Beginx()
|
||||||
TRUE
|
if err != nil {
|
||||||
ELSE
|
return nil, err
|
||||||
FALSE
|
}
|
||||||
END
|
defer finalizeTransactionX(transaction, &err)
|
||||||
WHERE
|
|
||||||
ChannelId = ?
|
|
||||||
AND (SchemeGuest = false OR SchemeGuest IS NULL)
|
|
||||||
`, strings.Join(userIDs, "', '"))
|
|
||||||
|
|
||||||
if _, err := s.GetMasterX().Exec(sql, channelID); err != nil {
|
// On MySQL it's not possible to update a table and select from it in the same query.
|
||||||
return errors.Wrap(err, "failed to update ChannelMembers")
|
// A SELECT and a UPDATE query are needed.
|
||||||
|
// Once we only support PostgreSQL, this can be done in a single query using RETURNING.
|
||||||
|
query, args, err := s.getQueryBuilder().
|
||||||
|
Select("*").
|
||||||
|
From("ChannelMembers").
|
||||||
|
Where(sq.Eq{"ChannelID": channelID}).
|
||||||
|
Where(sq.Or{sq.Eq{"SchemeGuest": false}, sq.Expr("SchemeGuest IS NULL")}).
|
||||||
|
Where(
|
||||||
|
sq.Or{
|
||||||
|
// New admins
|
||||||
|
sq.And{
|
||||||
|
sq.Eq{"SchemeAdmin": false},
|
||||||
|
sq.Eq{"UserId": adminIDs},
|
||||||
|
},
|
||||||
|
// Demoted admins
|
||||||
|
sq.And{
|
||||||
|
sq.Eq{"SchemeAdmin": true},
|
||||||
|
sq.NotEq{"UserId": adminIDs},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
).ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "channel_tosql")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
var updatedMembers []*model.ChannelMember
|
||||||
|
if err = transaction.Select(&updatedMembers, query, args...); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to get list of updated users")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update SchemeAdmin field as the data from the SQL is not updated yet
|
||||||
|
for _, member := range updatedMembers {
|
||||||
|
if slices.Contains(adminIDs, member.UserId) {
|
||||||
|
member.SchemeAdmin = true
|
||||||
|
} else {
|
||||||
|
member.SchemeAdmin = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query, args, err = s.getQueryBuilder().
|
||||||
|
Update("ChannelMembers").
|
||||||
|
Set("SchemeAdmin", sq.Case().When(sq.Eq{"UserId": adminIDs}, "true").Else("false")).
|
||||||
|
Where(sq.Eq{"ChannelId": channelID}).
|
||||||
|
Where(sq.Or{sq.Eq{"SchemeGuest": false}, sq.Expr("SchemeGuest IS NULL")}).ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "team_tosql")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = transaction.Exec(query, args...); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to update ChannelMembers")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = transaction.Commit(); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "commit_transaction")
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedMembers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SqlChannelStore) GroupSyncedChannelCount() (int64, error) {
|
func (s SqlChannelStore) GroupSyncedChannelCount() (int64, error) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package sqlstore
|
|||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
sq "github.com/mattermost/squirrel"
|
sq "github.com/mattermost/squirrel"
|
||||||
@@ -1591,23 +1592,74 @@ func (s SqlTeamStore) UserBelongsToTeams(userId string, teamIds []string) (bool,
|
|||||||
return c > 0, nil
|
return c > 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateMembersRole updates all the members of teamID in the userIds string array to be admins and sets all other
|
// UpdateMembersRole updates all the members of teamID in the adminIDs string array to be admins and sets all other
|
||||||
// users as not being admin.
|
// users as not being admin.
|
||||||
func (s SqlTeamStore) UpdateMembersRole(teamID string, userIDs []string) error {
|
// It returns the list of userIDs whose roles got updated.
|
||||||
|
func (s SqlTeamStore) UpdateMembersRole(teamID string, adminIDs []string) (_ []*model.TeamMember, err error) {
|
||||||
|
transaction, err := s.GetMasterX().Beginx()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer finalizeTransactionX(transaction, &err)
|
||||||
|
|
||||||
|
// On MySQL it's not possible to update a table and select from it in the same query.
|
||||||
|
// A SELECT and a UPDATE query are needed.
|
||||||
|
// Once we only support PostgreSQL, this can be done in a single query using RETURNING.
|
||||||
query, args, err := s.getQueryBuilder().
|
query, args, err := s.getQueryBuilder().
|
||||||
|
Select("*").
|
||||||
|
From("TeamMembers").
|
||||||
|
Where(sq.Eq{"TeamId": teamID, "DeleteAt": 0}).
|
||||||
|
Where(sq.Or{sq.Eq{"SchemeGuest": false}, sq.Expr("SchemeGuest IS NULL")}).
|
||||||
|
Where(
|
||||||
|
sq.Or{
|
||||||
|
// New admins
|
||||||
|
sq.And{
|
||||||
|
sq.Eq{"SchemeAdmin": false},
|
||||||
|
sq.Eq{"UserId": adminIDs},
|
||||||
|
},
|
||||||
|
// Demoted admins
|
||||||
|
sq.And{
|
||||||
|
sq.Eq{"SchemeAdmin": true},
|
||||||
|
sq.NotEq{"UserId": adminIDs},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
).ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "team_tosql")
|
||||||
|
}
|
||||||
|
|
||||||
|
var updatedMembers []*model.TeamMember
|
||||||
|
if err = transaction.Select(&updatedMembers, query, args...); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to get list of updated users")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update SchemeAdmin field as the data from the SQL is not updated yet
|
||||||
|
for _, member := range updatedMembers {
|
||||||
|
if slices.Contains(adminIDs, member.UserId) {
|
||||||
|
member.SchemeAdmin = true
|
||||||
|
} else {
|
||||||
|
member.SchemeAdmin = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query, args, err = s.getQueryBuilder().
|
||||||
Update("TeamMembers").
|
Update("TeamMembers").
|
||||||
Set("SchemeAdmin", sq.Case().When(sq.Eq{"UserId": userIDs}, "true").Else("false")).
|
Set("SchemeAdmin", sq.Case().When(sq.Eq{"UserId": adminIDs}, "true").Else("false")).
|
||||||
Where(sq.Eq{"TeamId": teamID, "DeleteAt": 0}).
|
Where(sq.Eq{"TeamId": teamID, "DeleteAt": 0}).
|
||||||
Where(sq.Or{sq.Eq{"SchemeGuest": false}, sq.Expr("SchemeGuest IS NULL")}).ToSql()
|
Where(sq.Or{sq.Eq{"SchemeGuest": false}, sq.Expr("SchemeGuest IS NULL")}).ToSql()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "team_tosql")
|
return nil, errors.Wrap(err, "team_tosql")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = s.GetMasterX().Exec(query, args...); err != nil {
|
if _, err = transaction.Exec(query, args...); err != nil {
|
||||||
return errors.Wrap(err, "failed to update TeamMembers")
|
return nil, errors.Wrap(err, "failed to update TeamMembers")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if err = transaction.Commit(); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "commit_transaction")
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedMembers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyTeamMemberViewRestrictionsFilter(query sq.SelectBuilder, restrictions *model.ViewUsersRestrictions) sq.SelectBuilder {
|
func applyTeamMemberViewRestrictionsFilter(query sq.SelectBuilder, restrictions *model.ViewUsersRestrictions) sq.SelectBuilder {
|
||||||
|
|||||||
@@ -168,7 +168,8 @@ type TeamStore interface {
|
|||||||
|
|
||||||
// UpdateMembersRole sets all of the given team members to admins and all of the other members of the team to
|
// UpdateMembersRole sets all of the given team members to admins and all of the other members of the team to
|
||||||
// non-admin members.
|
// non-admin members.
|
||||||
UpdateMembersRole(teamID string, userIDs []string) error
|
// It returns the list of userIDs whose roles got updated.
|
||||||
|
UpdateMembersRole(teamID string, adminIDs []string) ([]*model.TeamMember, error)
|
||||||
|
|
||||||
// GroupSyncedTeamCount returns the count of non-deleted group-constrained teams.
|
// GroupSyncedTeamCount returns the count of non-deleted group-constrained teams.
|
||||||
GroupSyncedTeamCount() (int64, error)
|
GroupSyncedTeamCount() (int64, error)
|
||||||
@@ -300,7 +301,8 @@ type ChannelStore interface {
|
|||||||
|
|
||||||
// UpdateMembersRole sets all of the given team members to admins and all of the other members of the team to
|
// UpdateMembersRole sets all of the given team members to admins and all of the other members of the team to
|
||||||
// non-admin members.
|
// non-admin members.
|
||||||
UpdateMembersRole(channelID string, userIDs []string) error
|
// It returns the list of userIDs whose roles got updated.
|
||||||
|
UpdateMembersRole(channelID string, userIDs []string) ([]*model.ChannelMember, error)
|
||||||
|
|
||||||
// GroupSyncedChannelCount returns the count of non-deleted group-constrained channels.
|
// GroupSyncedChannelCount returns the count of non-deleted group-constrained channels.
|
||||||
GroupSyncedChannelCount() (int64, error)
|
GroupSyncedChannelCount() (int64, error)
|
||||||
|
|||||||
@@ -3854,6 +3854,7 @@ func testChannelStoreGetAllChannels(t *testing.T, rctx request.CTX, ss store.Sto
|
|||||||
c1.DisplayName = "Channel1" + model.NewId()
|
c1.DisplayName = "Channel1" + model.NewId()
|
||||||
c1.Name = NewTestId()
|
c1.Name = NewTestId()
|
||||||
c1.Type = model.ChannelTypeOpen
|
c1.Type = model.ChannelTypeOpen
|
||||||
|
c1.GroupConstrained = model.NewPointer(true)
|
||||||
_, nErr := ss.Channel().Save(rctx, &c1, -1)
|
_, nErr := ss.Channel().Save(rctx, &c1, -1)
|
||||||
require.NoError(t, nErr)
|
require.NoError(t, nErr)
|
||||||
|
|
||||||
@@ -3939,6 +3940,19 @@ func testChannelStoreGetAllChannels(t *testing.T, rctx request.CTX, ss store.Sto
|
|||||||
list, nErr = ss.Channel().GetAllChannels(0, 10, store.ChannelSearchOpts{NotAssociatedToGroup: group.Id})
|
list, nErr = ss.Channel().GetAllChannels(0, 10, store.ChannelSearchOpts{NotAssociatedToGroup: group.Id})
|
||||||
require.NoError(t, nErr)
|
require.NoError(t, nErr)
|
||||||
assert.Len(t, list, 1)
|
assert.Len(t, list, 1)
|
||||||
|
assert.Equal(t, c3.Id, list[0].Id)
|
||||||
|
|
||||||
|
// GroupConstrained
|
||||||
|
list, nErr = ss.Channel().GetAllChannels(0, 10, store.ChannelSearchOpts{GroupConstrained: true})
|
||||||
|
require.NoError(t, nErr)
|
||||||
|
require.Len(t, list, 1)
|
||||||
|
assert.Equal(t, c1.Id, list[0].Id)
|
||||||
|
|
||||||
|
// ExcludeGroupConstrained
|
||||||
|
list, nErr = ss.Channel().GetAllChannels(0, 10, store.ChannelSearchOpts{ExcludeGroupConstrained: true})
|
||||||
|
require.NoError(t, nErr)
|
||||||
|
require.Len(t, list, 1)
|
||||||
|
assert.Equal(t, c3.Id, list[0].Id)
|
||||||
|
|
||||||
// Exclude channel names
|
// Exclude channel names
|
||||||
list, nErr = ss.Channel().GetAllChannels(0, 10, store.ChannelSearchOpts{ExcludeChannelNames: []string{c1.Name}})
|
list, nErr = ss.Channel().GetAllChannels(0, 10, store.ChannelSearchOpts{ExcludeChannelNames: []string{c1.Name}})
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func TestGroupStore(t *testing.T, rctx request.CTX, ss store.Store) {
|
|||||||
t.Run("AdminRoleGroupsForSyncableMember_Team", func(t *testing.T) { groupTestAdminRoleGroupsForSyncableMemberTeam(t, rctx, ss) })
|
t.Run("AdminRoleGroupsForSyncableMember_Team", func(t *testing.T) { groupTestAdminRoleGroupsForSyncableMemberTeam(t, rctx, ss) })
|
||||||
t.Run("PermittedSyncableAdmins_Team", func(t *testing.T) { groupTestPermittedSyncableAdminsTeam(t, rctx, ss) })
|
t.Run("PermittedSyncableAdmins_Team", func(t *testing.T) { groupTestPermittedSyncableAdminsTeam(t, rctx, ss) })
|
||||||
t.Run("PermittedSyncableAdmins_Channel", func(t *testing.T) { groupTestPermittedSyncableAdminsChannel(t, rctx, ss) })
|
t.Run("PermittedSyncableAdmins_Channel", func(t *testing.T) { groupTestPermittedSyncableAdminsChannel(t, rctx, ss) })
|
||||||
t.Run("UpdateMembersRole_Team", func(t *testing.T) { groupTestpUpdateMembersRoleTeam(t, rctx, ss) })
|
t.Run("UpdateMembersRole_Team", func(t *testing.T) { groupTestUpdateMembersRoleTeam(t, rctx, ss) })
|
||||||
t.Run("UpdateMembersRole_Channel", func(t *testing.T) { groupTestpUpdateMembersRoleChannel(t, rctx, ss) })
|
t.Run("UpdateMembersRole_Channel", func(t *testing.T) { groupTestpUpdateMembersRoleChannel(t, rctx, ss) })
|
||||||
|
|
||||||
t.Run("GroupCount", func(t *testing.T) { groupTestGroupCount(t, rctx, ss) })
|
t.Run("GroupCount", func(t *testing.T) { groupTestGroupCount(t, rctx, ss) })
|
||||||
@@ -4739,7 +4739,7 @@ func groupTestPermittedSyncableAdminsChannel(t *testing.T, rctx request.CTX, ss
|
|||||||
require.ElementsMatch(t, []string{user3.Id}, actualUserIDs)
|
require.ElementsMatch(t, []string{user3.Id}, actualUserIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func groupTestpUpdateMembersRoleTeam(t *testing.T, rctx request.CTX, ss store.Store) {
|
func groupTestUpdateMembersRoleTeam(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||||
team := &model.Team{
|
team := &model.Team{
|
||||||
DisplayName: "Name",
|
DisplayName: "Name",
|
||||||
Description: "Some description",
|
Description: "Some description",
|
||||||
@@ -4759,6 +4759,7 @@ func groupTestpUpdateMembersRoleTeam(t *testing.T, rctx request.CTX, ss store.St
|
|||||||
}
|
}
|
||||||
user1, err = ss.User().Save(rctx, user1)
|
user1, err = ss.User().Save(rctx, user1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
t.Log("Created user1", user1.Id)
|
||||||
|
|
||||||
user2 := &model.User{
|
user2 := &model.User{
|
||||||
Email: MakeEmail(),
|
Email: MakeEmail(),
|
||||||
@@ -4766,6 +4767,7 @@ func groupTestpUpdateMembersRoleTeam(t *testing.T, rctx request.CTX, ss store.St
|
|||||||
}
|
}
|
||||||
user2, err = ss.User().Save(rctx, user2)
|
user2, err = ss.User().Save(rctx, user2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
t.Log("Created user2", user2.Id)
|
||||||
|
|
||||||
user3 := &model.User{
|
user3 := &model.User{
|
||||||
Email: MakeEmail(),
|
Email: MakeEmail(),
|
||||||
@@ -4773,6 +4775,7 @@ func groupTestpUpdateMembersRoleTeam(t *testing.T, rctx request.CTX, ss store.St
|
|||||||
}
|
}
|
||||||
user3, err = ss.User().Save(rctx, user3)
|
user3, err = ss.User().Save(rctx, user3)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
t.Log("Created user3", user3.Id)
|
||||||
|
|
||||||
user4 := &model.User{
|
user4 := &model.User{
|
||||||
Email: MakeEmail(),
|
Email: MakeEmail(),
|
||||||
@@ -4780,6 +4783,7 @@ func groupTestpUpdateMembersRoleTeam(t *testing.T, rctx request.CTX, ss store.St
|
|||||||
}
|
}
|
||||||
user4, err = ss.User().Save(rctx, user4)
|
user4, err = ss.User().Save(rctx, user4)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
t.Log("Created user4", user4.Id)
|
||||||
|
|
||||||
for _, user := range []*model.User{user1, user2, user3} {
|
for _, user := range []*model.User{user1, user2, user3} {
|
||||||
_, nErr := ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: team.Id, UserId: user.Id}, 9999)
|
_, nErr := ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: team.Id, UserId: user.Id}, 9999)
|
||||||
@@ -4790,53 +4794,73 @@ func groupTestpUpdateMembersRoleTeam(t *testing.T, rctx request.CTX, ss store.St
|
|||||||
require.NoError(t, nErr)
|
require.NoError(t, nErr)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
testName string
|
testName string
|
||||||
inUserIDs []string
|
newAdmins []string
|
||||||
targetSchemeAdminValue bool
|
expectedUpdatedUsers []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"Given users are admins",
|
"Two new admins",
|
||||||
|
[]string{user1.Id, user2.Id},
|
||||||
[]string{user1.Id, user2.Id},
|
[]string{user1.Id, user2.Id},
|
||||||
true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Given users are members",
|
"Demote one admin",
|
||||||
|
[]string{user1.Id},
|
||||||
[]string{user2.Id},
|
[]string{user2.Id},
|
||||||
false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Non-given users are admins",
|
"Operation is idempotent",
|
||||||
[]string{user2.Id},
|
[]string{user1.Id},
|
||||||
false,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Non-given users are members",
|
"Promote a team member",
|
||||||
[]string{user2.Id},
|
[]string{user1.Id, user3.Id},
|
||||||
false,
|
[]string{user3.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Guests never get promoted",
|
||||||
|
[]string{user1.Id, user3.Id, user4.Id},
|
||||||
|
nil,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.testName, func(t *testing.T) {
|
t.Run(tt.testName, func(t *testing.T) {
|
||||||
err = ss.Team().UpdateMembersRole(team.Id, tt.inUserIDs)
|
var updatedMembers []*model.TeamMember
|
||||||
|
updatedMembers, err = ss.Team().UpdateMembersRole(team.Id, tt.newAdmins)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var updatedUserIDs []string
|
||||||
|
for _, member := range updatedMembers {
|
||||||
|
assert.False(t, member.SchemeGuest, fmt.Sprintf("userID: %s", member.UserId))
|
||||||
|
|
||||||
|
if slices.Contains(tt.newAdmins, member.UserId) {
|
||||||
|
assert.True(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
||||||
|
} else {
|
||||||
|
assert.False(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedUserIDs = append(updatedUserIDs, member.UserId)
|
||||||
|
}
|
||||||
|
assert.ElementsMatch(t, tt.expectedUpdatedUsers, updatedUserIDs)
|
||||||
|
|
||||||
members, err := ss.Team().GetMembers(team.Id, 0, 100, nil)
|
members, err := ss.Team().GetMembers(team.Id, 0, 100, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.GreaterOrEqual(t, len(members), 4) // sanity check for team membership
|
assert.GreaterOrEqual(t, len(members), 4) // sanity check for team membership
|
||||||
|
|
||||||
for _, member := range members {
|
for _, member := range members {
|
||||||
if slices.Contains(tt.inUserIDs, member.UserId) {
|
|
||||||
require.True(t, member.SchemeAdmin)
|
|
||||||
} else {
|
|
||||||
require.False(t, member.SchemeAdmin)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure guest account never changes.
|
// Ensure guest account never changes.
|
||||||
if member.UserId == user4.Id {
|
if member.UserId == user4.Id {
|
||||||
require.False(t, member.SchemeUser)
|
assert.False(t, member.SchemeUser, fmt.Sprintf("userID: %s", member.UserId))
|
||||||
require.False(t, member.SchemeAdmin)
|
assert.False(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
||||||
require.True(t, member.SchemeGuest)
|
assert.True(t, member.SchemeGuest, fmt.Sprintf("userID: %s", member.UserId))
|
||||||
|
} else {
|
||||||
|
if slices.Contains(tt.newAdmins, member.UserId) {
|
||||||
|
assert.True(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
||||||
|
} else {
|
||||||
|
assert.False(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -4859,6 +4883,7 @@ func groupTestpUpdateMembersRoleChannel(t *testing.T, rctx request.CTX, ss store
|
|||||||
}
|
}
|
||||||
user1, err = ss.User().Save(rctx, user1)
|
user1, err = ss.User().Save(rctx, user1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
t.Log("Created user1", user1.Id)
|
||||||
|
|
||||||
user2 := &model.User{
|
user2 := &model.User{
|
||||||
Email: MakeEmail(),
|
Email: MakeEmail(),
|
||||||
@@ -4866,6 +4891,7 @@ func groupTestpUpdateMembersRoleChannel(t *testing.T, rctx request.CTX, ss store
|
|||||||
}
|
}
|
||||||
user2, err = ss.User().Save(rctx, user2)
|
user2, err = ss.User().Save(rctx, user2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
t.Log("Created user2", user2.Id)
|
||||||
|
|
||||||
user3 := &model.User{
|
user3 := &model.User{
|
||||||
Email: MakeEmail(),
|
Email: MakeEmail(),
|
||||||
@@ -4873,6 +4899,7 @@ func groupTestpUpdateMembersRoleChannel(t *testing.T, rctx request.CTX, ss store
|
|||||||
}
|
}
|
||||||
user3, err = ss.User().Save(rctx, user3)
|
user3, err = ss.User().Save(rctx, user3)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
t.Log("Created user3", user3.Id)
|
||||||
|
|
||||||
user4 := &model.User{
|
user4 := &model.User{
|
||||||
Email: MakeEmail(),
|
Email: MakeEmail(),
|
||||||
@@ -4880,6 +4907,7 @@ func groupTestpUpdateMembersRoleChannel(t *testing.T, rctx request.CTX, ss store
|
|||||||
}
|
}
|
||||||
user4, err = ss.User().Save(rctx, user4)
|
user4, err = ss.User().Save(rctx, user4)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
t.Log("Created user4", user4.Id)
|
||||||
|
|
||||||
for _, user := range []*model.User{user1, user2, user3} {
|
for _, user := range []*model.User{user1, user2, user3} {
|
||||||
_, err = ss.Channel().SaveMember(rctx, &model.ChannelMember{
|
_, err = ss.Channel().SaveMember(rctx, &model.ChannelMember{
|
||||||
@@ -4899,54 +4927,73 @@ func groupTestpUpdateMembersRoleChannel(t *testing.T, rctx request.CTX, ss store
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
testName string
|
testName string
|
||||||
inUserIDs []string
|
newAdmins []string
|
||||||
targetSchemeAdminValue bool
|
expectedUpdatedUsers []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"Given users are admins",
|
"Two new admins",
|
||||||
|
[]string{user1.Id, user2.Id},
|
||||||
[]string{user1.Id, user2.Id},
|
[]string{user1.Id, user2.Id},
|
||||||
true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Given users are members",
|
"Demote one admin",
|
||||||
|
[]string{user1.Id},
|
||||||
[]string{user2.Id},
|
[]string{user2.Id},
|
||||||
false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Non-given users are admins",
|
"Operation is idempotent",
|
||||||
[]string{user2.Id},
|
[]string{user1.Id},
|
||||||
false,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Non-given users are members",
|
"Promote a team member",
|
||||||
[]string{user2.Id},
|
[]string{user1.Id, user3.Id},
|
||||||
false,
|
[]string{user3.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Guests never get promoted",
|
||||||
|
[]string{user1.Id, user3.Id, user4.Id},
|
||||||
|
nil,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.testName, func(t *testing.T) {
|
t.Run(tt.testName, func(t *testing.T) {
|
||||||
err = ss.Channel().UpdateMembersRole(channel.Id, tt.inUserIDs)
|
var updatedMemmbers []*model.ChannelMember
|
||||||
|
updatedMemmbers, err = ss.Channel().UpdateMembersRole(channel.Id, tt.newAdmins)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var updatedUserIDs []string
|
||||||
|
for _, member := range updatedMemmbers {
|
||||||
|
assert.False(t, member.SchemeGuest, fmt.Sprintf("userID: %s", member.UserId))
|
||||||
|
|
||||||
|
if slices.Contains(tt.newAdmins, member.UserId) {
|
||||||
|
assert.True(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
||||||
|
} else {
|
||||||
|
assert.False(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedUserIDs = append(updatedUserIDs, member.UserId)
|
||||||
|
}
|
||||||
|
assert.ElementsMatch(t, tt.expectedUpdatedUsers, updatedUserIDs)
|
||||||
|
|
||||||
members, err := ss.Channel().GetMembers(channel.Id, 0, 100)
|
members, err := ss.Channel().GetMembers(channel.Id, 0, 100)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
assert.GreaterOrEqual(t, len(members), 4) // sanity check for channel membership
|
||||||
require.GreaterOrEqual(t, len(members), 4) // sanity check for channel membership
|
|
||||||
|
|
||||||
for _, member := range members {
|
for _, member := range members {
|
||||||
if slices.Contains(tt.inUserIDs, member.UserId) {
|
|
||||||
require.True(t, member.SchemeAdmin)
|
|
||||||
} else {
|
|
||||||
require.False(t, member.SchemeAdmin)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure guest account never changes.
|
// Ensure guest account never changes.
|
||||||
if member.UserId == user4.Id {
|
if member.UserId == user4.Id {
|
||||||
require.False(t, member.SchemeUser)
|
assert.False(t, member.SchemeUser, fmt.Sprintf("userID: %s", member.UserId))
|
||||||
require.False(t, member.SchemeAdmin)
|
assert.False(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
||||||
require.True(t, member.SchemeGuest)
|
assert.True(t, member.SchemeGuest, fmt.Sprintf("userID: %s", member.UserId))
|
||||||
|
} else {
|
||||||
|
if slices.Contains(tt.newAdmins, member.UserId) {
|
||||||
|
assert.True(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
||||||
|
} else {
|
||||||
|
assert.False(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2915,21 +2915,33 @@ func (_m *ChannelStore) UpdateMemberNotifyProps(channelID string, userID string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateMembersRole provides a mock function with given fields: channelID, userIDs
|
// UpdateMembersRole provides a mock function with given fields: channelID, userIDs
|
||||||
func (_m *ChannelStore) UpdateMembersRole(channelID string, userIDs []string) error {
|
func (_m *ChannelStore) UpdateMembersRole(channelID string, userIDs []string) ([]*model.ChannelMember, error) {
|
||||||
ret := _m.Called(channelID, userIDs)
|
ret := _m.Called(channelID, userIDs)
|
||||||
|
|
||||||
if len(ret) == 0 {
|
if len(ret) == 0 {
|
||||||
panic("no return value specified for UpdateMembersRole")
|
panic("no return value specified for UpdateMembersRole")
|
||||||
}
|
}
|
||||||
|
|
||||||
var r0 error
|
var r0 []*model.ChannelMember
|
||||||
if rf, ok := ret.Get(0).(func(string, []string) error); ok {
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(string, []string) ([]*model.ChannelMember, error)); ok {
|
||||||
|
return rf(channelID, userIDs)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(string, []string) []*model.ChannelMember); ok {
|
||||||
r0 = rf(channelID, userIDs)
|
r0 = rf(channelID, userIDs)
|
||||||
} else {
|
} else {
|
||||||
r0 = ret.Error(0)
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*model.ChannelMember)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return r0
|
if rf, ok := ret.Get(1).(func(string, []string) error); ok {
|
||||||
|
r1 = rf(channelID, userIDs)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateMultipleMembers provides a mock function with given fields: members
|
// UpdateMultipleMembers provides a mock function with given fields: members
|
||||||
|
|||||||
@@ -1336,22 +1336,34 @@ func (_m *TeamStore) UpdateMember(rctx request.CTX, member *model.TeamMember) (*
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateMembersRole provides a mock function with given fields: teamID, userIDs
|
// UpdateMembersRole provides a mock function with given fields: teamID, adminIDs
|
||||||
func (_m *TeamStore) UpdateMembersRole(teamID string, userIDs []string) error {
|
func (_m *TeamStore) UpdateMembersRole(teamID string, adminIDs []string) ([]*model.TeamMember, error) {
|
||||||
ret := _m.Called(teamID, userIDs)
|
ret := _m.Called(teamID, adminIDs)
|
||||||
|
|
||||||
if len(ret) == 0 {
|
if len(ret) == 0 {
|
||||||
panic("no return value specified for UpdateMembersRole")
|
panic("no return value specified for UpdateMembersRole")
|
||||||
}
|
}
|
||||||
|
|
||||||
var r0 error
|
var r0 []*model.TeamMember
|
||||||
if rf, ok := ret.Get(0).(func(string, []string) error); ok {
|
var r1 error
|
||||||
r0 = rf(teamID, userIDs)
|
if rf, ok := ret.Get(0).(func(string, []string) ([]*model.TeamMember, error)); ok {
|
||||||
|
return rf(teamID, adminIDs)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(string, []string) []*model.TeamMember); ok {
|
||||||
|
r0 = rf(teamID, adminIDs)
|
||||||
} else {
|
} else {
|
||||||
r0 = ret.Error(0)
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*model.TeamMember)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return r0
|
if rf, ok := ret.Get(1).(func(string, []string) error); ok {
|
||||||
|
r1 = rf(teamID, adminIDs)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateMultipleMembers provides a mock function with given fields: members
|
// UpdateMultipleMembers provides a mock function with given fields: members
|
||||||
|
|||||||
@@ -2366,10 +2366,10 @@ func (s *TimerLayerChannelStore) UpdateMemberNotifyProps(channelID string, userI
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TimerLayerChannelStore) UpdateMembersRole(channelID string, userIDs []string) error {
|
func (s *TimerLayerChannelStore) UpdateMembersRole(channelID string, userIDs []string) ([]*model.ChannelMember, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
err := s.ChannelStore.UpdateMembersRole(channelID, userIDs)
|
result, err := s.ChannelStore.UpdateMembersRole(channelID, userIDs)
|
||||||
|
|
||||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||||
if s.Root.Metrics != nil {
|
if s.Root.Metrics != nil {
|
||||||
@@ -2379,7 +2379,7 @@ func (s *TimerLayerChannelStore) UpdateMembersRole(channelID string, userIDs []s
|
|||||||
}
|
}
|
||||||
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.UpdateMembersRole", success, elapsed)
|
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.UpdateMembersRole", success, elapsed)
|
||||||
}
|
}
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TimerLayerChannelStore) UpdateMultipleMembers(members []*model.ChannelMember) ([]*model.ChannelMember, error) {
|
func (s *TimerLayerChannelStore) UpdateMultipleMembers(members []*model.ChannelMember) ([]*model.ChannelMember, error) {
|
||||||
@@ -9495,10 +9495,10 @@ func (s *TimerLayerTeamStore) UpdateMember(rctx request.CTX, member *model.TeamM
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TimerLayerTeamStore) UpdateMembersRole(teamID string, userIDs []string) error {
|
func (s *TimerLayerTeamStore) UpdateMembersRole(teamID string, adminIDs []string) ([]*model.TeamMember, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
err := s.TeamStore.UpdateMembersRole(teamID, userIDs)
|
result, err := s.TeamStore.UpdateMembersRole(teamID, adminIDs)
|
||||||
|
|
||||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||||
if s.Root.Metrics != nil {
|
if s.Root.Metrics != nil {
|
||||||
@@ -9508,7 +9508,7 @@ func (s *TimerLayerTeamStore) UpdateMembersRole(teamID string, userIDs []string)
|
|||||||
}
|
}
|
||||||
s.Root.Metrics.ObserveStoreMethodDuration("TeamStore.UpdateMembersRole", success, elapsed)
|
s.Root.Metrics.ObserveStoreMethodDuration("TeamStore.UpdateMembersRole", success, elapsed)
|
||||||
}
|
}
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TimerLayerTeamStore) UpdateMultipleMembers(members []*model.TeamMember) ([]*model.TeamMember, error) {
|
func (s *TimerLayerTeamStore) UpdateMultipleMembers(members []*model.TeamMember) ([]*model.TeamMember, error) {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user