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>
Этот коммит содержится в:
Kyriakos Z
2023-04-19 15:20:34 +03:00
коммит произвёл GitHub
родитель c34a50a6c7
Коммит a24111f9bd
16 изменённых файлов: 426 добавлений и 38 удалений

Просмотреть файл

@@ -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) {