MM-40148: threadsOnly query param for user threads (#19833)

* MM-40148: threadsOnly query param for user threads

Currently we always calculate counts when fetching user threads.
Those counts include total, unread replies, and unread mentions,
and are potentially expensive to calculate.

This commit adds a new query param 'threadsOnly' which won't calculate
any counts and just return threads.

Co-authored-by: koox00 <3829551+koox00@users.noreply.github.com>
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Ashish Bhate
2022-04-04 17:50:13 +05:30
коммит произвёл GitHub
родитель 27bd16e5f5
Коммит 9521797b15
6 изменённых файлов: 149 добавлений и 34 удалений

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

@@ -3008,14 +3008,15 @@ func getThreadsForUser(c *Context, w http.ResponseWriter, r *http.Request) {
}
options := model.GetUserThreadsOpts{
Since: 0,
Before: "",
After: "",
PageSize: uint64(c.Params.PerPage),
Unread: false,
Extended: false,
Deleted: false,
TotalsOnly: false,
Since: 0,
Before: "",
After: "",
PageSize: uint64(c.Params.PerPage),
Unread: false,
Extended: false,
Deleted: false,
TotalsOnly: false,
ThreadsOnly: false,
}
sinceString := r.URL.Query().Get("since")
@@ -3030,21 +3031,30 @@ func getThreadsForUser(c *Context, w http.ResponseWriter, r *http.Request) {
options.Before = r.URL.Query().Get("before")
options.After = r.URL.Query().Get("after")
totalsOnlyStr := r.URL.Query().Get("totalsOnly")
threadsOnlyStr := r.URL.Query().Get("threadsOnly")
options.TotalsOnly, _ = strconv.ParseBool(totalsOnlyStr)
options.ThreadsOnly, _ = strconv.ParseBool(threadsOnlyStr)
// parameters are mutually exclusive
if options.Before != "" && options.After != "" {
c.Err = model.NewAppError("api.getThreadsForUser", "api.getThreadsForUser.bad_params", nil, "", http.StatusBadRequest)
return
}
// parameters are mutually exclusive
if options.TotalsOnly && options.ThreadsOnly {
c.Err = model.NewAppError("api.getThreadsForUser", "api.getThreadsForUser.bad_only_params", nil, "", http.StatusBadRequest)
return
}
deletedStr := r.URL.Query().Get("deleted")
unreadStr := r.URL.Query().Get("unread")
extendedStr := r.URL.Query().Get("extended")
totalsOnlyStr := r.URL.Query().Get("totalsOnly")
options.Deleted, _ = strconv.ParseBool(deletedStr)
options.Unread, _ = strconv.ParseBool(unreadStr)
options.Extended, _ = strconv.ParseBool(extendedStr)
options.TotalsOnly, _ = strconv.ParseBool(totalsOnlyStr)
threads, err := c.App.GetThreadsForUser(c.Params.UserId, c.Params.TeamId, options)
if err != nil {

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

@@ -5754,6 +5754,96 @@ func TestGetThreadsForUser(t *testing.T) {
require.Len(t, uss3.Threads, 0)
})
t.Run("totalsOnly param", func(t *testing.T) {
client := th.Client
sysadminClient := th.SystemAdminClient
var rootIds []*model.Post
for i := 0; i < 10; i++ {
rpost, resp, err := client.CreatePost(&model.Post{ChannelId: th.BasicChannel.Id, Message: "testMsg"})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
rootIds = append(rootIds, rpost)
if i%2 == 0 {
_, resp, err = client.CreatePost(&model.Post{ChannelId: th.BasicChannel.Id, Message: "testReply", RootId: rpost.Id})
} else {
_, resp, err = sysadminClient.CreatePost(&model.Post{ChannelId: th.BasicChannel.Id, Message: "testReply @" + th.BasicUser.Username, RootId: rpost.Id})
}
require.NoError(t, err)
CheckCreatedStatus(t, resp)
}
defer th.App.Srv().Store.Post().PermanentDeleteByUser(th.BasicUser.Id)
defer th.App.Srv().Store.Post().PermanentDeleteByUser(th.SystemAdminUser.Id)
uss, _, err := th.Client.GetUserThreads(th.BasicUser.Id, th.BasicTeam.Id, model.GetUserThreadsOpts{
Deleted: false,
TotalsOnly: true,
PageSize: 30,
})
require.NoError(t, err)
require.Len(t, uss.Threads, 0)
require.Len(t, rootIds, 10)
require.Equal(t, int64(10), uss.Total)
require.Equal(t, int64(5), uss.TotalUnreadThreads)
require.Equal(t, int64(5), uss.TotalUnreadMentions)
})
t.Run("threadsOnly param", func(t *testing.T) {
client := th.Client
sysadminClient := th.SystemAdminClient
var rootIds []*model.Post
for i := 0; i < 10; i++ {
rpost, resp, err := client.CreatePost(&model.Post{ChannelId: th.BasicChannel.Id, Message: "testMsg"})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
rootIds = append(rootIds, rpost)
if i%2 == 0 {
_, resp, err = client.CreatePost(&model.Post{ChannelId: th.BasicChannel.Id, Message: "testReply", RootId: rpost.Id})
} else {
_, resp, err = sysadminClient.CreatePost(&model.Post{ChannelId: th.BasicChannel.Id, Message: "testReply @" + th.BasicUser.Username, RootId: rpost.Id})
}
require.NoError(t, err)
CheckCreatedStatus(t, resp)
}
defer th.App.Srv().Store.Post().PermanentDeleteByUser(th.BasicUser.Id)
defer th.App.Srv().Store.Post().PermanentDeleteByUser(th.SystemAdminUser.Id)
uss, _, err := th.Client.GetUserThreads(th.BasicUser.Id, th.BasicTeam.Id, model.GetUserThreadsOpts{
Deleted: false,
ThreadsOnly: true,
PageSize: 30,
})
require.NoError(t, err)
require.Len(t, rootIds, 10)
require.Len(t, uss.Threads, 10)
require.Equal(t, int64(0), uss.Total)
require.Equal(t, int64(0), uss.TotalUnreadThreads)
require.Equal(t, int64(0), uss.TotalUnreadMentions)
require.Equal(t, int64(1), uss.Threads[0].ReplyCount)
require.Equal(t, rootIds[9].Id, uss.Threads[0].PostId)
require.Equal(t, th.SystemAdminUser.Id, uss.Threads[0].Participants[0].Id)
require.Equal(t, th.BasicUser.Id, uss.Threads[1].Participants[0].Id)
})
t.Run("setting both threadsOnly, and totalsOnly params is not allowed", func(t *testing.T) {
defer th.App.Srv().Store.Post().PermanentDeleteByUser(th.BasicUser.Id)
_, resp, err := th.Client.GetUserThreads(th.BasicUser.Id, th.BasicTeam.Id, model.GetUserThreadsOpts{
ThreadsOnly: true,
TotalsOnly: true,
PageSize: 30,
})
require.Error(t, err)
checkHTTPStatus(t, resp, http.StatusBadRequest)
})
t.Run("editing or reacting to reply post does not make thread unread", func(t *testing.T) {
client := th.Client