MM-45009: Delete ThreadMemberships from "left" channels (#22559)
* MM-50550: Filter out threads from "left" channels v2 Currently leaving a channel doesn't affect the thread memberships of that user/channel combination. This PR aims to filter out all threads from those channels for the user. Adds a DeleteAt column in the ThreadMemberships table, and filter out all thread memberships that are "deleted". Each time a user leaves a channel all thread memberships are going to be marked as deleted, and when a user joins a channel again all those existing thread memberships will be re-instantiated. Adds a migration to mark all existing thread memberships as deleted depending on whether there exists a channel membership for that channel/user. * Added migration files into list * Fixes tests * Fixes case where DeleteAt would be null * Guard thread API endpoints with appropriate perms * Deletes ThreadMembership rows upon leaving channel * Minor style changes * Use NoTranslation error * Refactors tests * Adds API tests to assert permissions on Team * Adds tests, and fixes migrations * Fixes test description * Fix test * Removes check on DM/GMs * Change the MySQL query in the migration --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
@@ -2518,6 +2518,9 @@ func (a *App) removeUserFromChannel(c request.CTX, userIDToRemove string, remove
|
||||
if err := a.Srv().Store().ChannelMemberHistory().LogLeaveEvent(userIDToRemove, channel.Id, model.GetMillis()); err != nil {
|
||||
return model.NewAppError("removeUserFromChannel", "app.channel_member_history.log_leave_event.internal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
if err := a.Srv().Store().Thread().DeleteMembershipsForChannel(userIDToRemove, channel.Id); err != nil {
|
||||
return model.NewAppError("removeUserFromChannel", model.NoTranslation, nil, "failed to delete threadmemberships upon leaving channel", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
if isGuest {
|
||||
currentMembers, err := a.GetChannelMembersForUser(c, channel.TeamId, userIDToRemove)
|
||||
|
||||
@@ -609,6 +609,85 @@ func TestLeaveDefaultChannel(t *testing.T) {
|
||||
_, err = th.App.GetChannelMember(th.Context, townSquare.Id, guest.Id)
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
|
||||
t.Run("Trying to leave the default channel should not delete thread memberships", func(t *testing.T) {
|
||||
post := &model.Post{
|
||||
ChannelId: townSquare.Id,
|
||||
Message: "root post",
|
||||
UserId: th.BasicUser.Id,
|
||||
}
|
||||
rpost, err := th.App.CreatePost(th.Context, post, th.BasicChannel, false, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
reply := &model.Post{
|
||||
ChannelId: townSquare.Id,
|
||||
Message: "reply post",
|
||||
UserId: th.BasicUser.Id,
|
||||
RootId: rpost.Id,
|
||||
}
|
||||
_, err = th.App.CreatePost(th.Context, reply, th.BasicChannel, false, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
threads, err := th.App.GetThreadsForUser(th.BasicUser.Id, townSquare.TeamId, model.GetUserThreadsOpts{})
|
||||
require.Nil(t, err)
|
||||
require.Len(t, threads.Threads, 1)
|
||||
|
||||
err = th.App.LeaveChannel(th.Context, townSquare.Id, th.BasicUser.Id)
|
||||
assert.NotNil(t, err, "It should fail to remove a regular user from the default channel")
|
||||
assert.Equal(t, err.Id, "api.channel.remove.default.app_error")
|
||||
|
||||
threads, err = th.App.GetThreadsForUser(th.BasicUser.Id, townSquare.TeamId, model.GetUserThreadsOpts{})
|
||||
require.Nil(t, err)
|
||||
require.Len(t, threads.Threads, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLeaveChannel(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
createThread := func(channel *model.Channel) (rpost *model.Post) {
|
||||
t.Helper()
|
||||
post := &model.Post{
|
||||
ChannelId: channel.Id,
|
||||
Message: "root post",
|
||||
UserId: th.BasicUser.Id,
|
||||
}
|
||||
|
||||
rpost, err := th.App.CreatePost(th.Context, post, th.BasicChannel, false, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
reply := &model.Post{
|
||||
ChannelId: channel.Id,
|
||||
Message: "reply post",
|
||||
UserId: th.BasicUser.Id,
|
||||
RootId: rpost.Id,
|
||||
}
|
||||
_, err = th.App.CreatePost(th.Context, reply, th.BasicChannel, false, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
return rpost
|
||||
}
|
||||
|
||||
t.Run("thread memberships are deleted", func(t *testing.T) {
|
||||
createThread(th.BasicChannel)
|
||||
channel2 := th.createChannel(th.Context, th.BasicTeam, model.ChannelTypeOpen)
|
||||
createThread(channel2)
|
||||
|
||||
threads, err := th.App.GetThreadsForUser(th.BasicUser.Id, th.BasicChannel.TeamId, model.GetUserThreadsOpts{})
|
||||
require.Nil(t, err)
|
||||
require.Len(t, threads.Threads, 2)
|
||||
|
||||
err = th.App.LeaveChannel(th.Context, th.BasicChannel.Id, th.BasicUser.Id)
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = th.App.GetChannelMember(th.Context, th.BasicChannel.Id, th.BasicUser.Id)
|
||||
require.NotNil(t, err, "It should remove channel membership")
|
||||
|
||||
threads, err = th.App.GetThreadsForUser(th.BasicUser.Id, th.BasicChannel.TeamId, model.GetUserThreadsOpts{})
|
||||
require.Nil(t, err)
|
||||
require.Len(t, threads.Threads, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLeaveLastChannel(t *testing.T) {
|
||||
|
||||
Ссылка в новой задаче
Block a user