MM-29988 - Update exising Post APIs with collapsed threads (#16503)

Этот коммит содержится в:
Eli Yukelzon
2021-01-14 13:46:27 +02:00
коммит произвёл GitHub
родитель 2e1b578ccd
Коммит b0bddeb5e6
28 изменённых файлов: 616 добавлений и 257 удалений

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

@@ -30,7 +30,7 @@ func TestEchoCommand(t *testing.T) {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
p1 := Client.Must(Client.GetPostsForChannel(channel1.Id, 0, 2, "")).(*model.PostList) p1 := Client.Must(Client.GetPostsForChannel(channel1.Id, 0, 2, "", false)).(*model.PostList)
require.Len(t, p1.Order, 2, "Echo command failed to send") require.Len(t, p1.Order, 2, "Echo command failed to send")
} }
@@ -302,7 +302,7 @@ func TestMeCommand(t *testing.T) {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
p1 := Client.Must(Client.GetPostsForChannel(channel.Id, 0, 2, "")).(*model.PostList) p1 := Client.Must(Client.GetPostsForChannel(channel.Id, 0, 2, "", false)).(*model.PostList)
require.Len(t, p1.Order, 2, "Command failed to send") require.Len(t, p1.Order, 2, "Command failed to send")
pt := p1.Posts[p1.Order[0]].Type pt := p1.Posts[p1.Order[0]].Type
@@ -391,7 +391,7 @@ func TestShrugCommand(t *testing.T) {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
p1 := Client.Must(Client.GetPostsForChannel(channel.Id, 0, 2, "")).(*model.PostList) p1 := Client.Must(Client.GetPostsForChannel(channel.Id, 0, 2, "", false)).(*model.PostList)
require.Len(t, p1.Order, 2, "Command failed to send") require.Len(t, p1.Order, 2, "Command failed to send")
require.Equal(t, `¯\\\_(ツ)\_/¯`, p1.Posts[p1.Order[0]].Message, "invalid shrug response") require.Equal(t, `¯\\\_(ツ)\_/¯`, p1.Posts[p1.Order[0]].Message, "invalid shrug response")
} }

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

@@ -158,7 +158,8 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) {
} }
} }
skipFetchThreads := r.URL.Query().Get("skipFetchThreads") == "true" skipFetchThreads := r.URL.Query().Get("skipFetchThreads") == "true"
collapsedThreads := r.URL.Query().Get("collapsedThreads") == "true"
collapsedThreadsExtended := r.URL.Query().Get("collapsedThreadsExtended") == "true"
channelId := c.Params.ChannelId channelId := c.Params.ChannelId
page := c.Params.Page page := c.Params.Page
perPage := c.Params.PerPage perPage := c.Params.PerPage
@@ -173,31 +174,31 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) {
etag := "" etag := ""
if since > 0 { if since > 0 {
list, err = c.App.GetPostsSince(model.GetPostsSinceOptions{ChannelId: channelId, Time: since, SkipFetchThreads: skipFetchThreads}) list, err = c.App.GetPostsSince(model.GetPostsSinceOptions{ChannelId: channelId, Time: since, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, CollapsedThreadsExtended: collapsedThreadsExtended})
} else if len(afterPost) > 0 { } else if len(afterPost) > 0 {
etag = c.App.GetPostsEtag(channelId) etag = c.App.GetPostsEtag(channelId, collapsedThreads)
if c.HandleEtag(etag, "Get Posts After", w, r) { if c.HandleEtag(etag, "Get Posts After", w, r) {
return return
} }
list, err = c.App.GetPostsAfterPost(model.GetPostsOptions{ChannelId: channelId, PostId: afterPost, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads}) list, err = c.App.GetPostsAfterPost(model.GetPostsOptions{ChannelId: channelId, PostId: afterPost, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads})
} else if len(beforePost) > 0 { } else if len(beforePost) > 0 {
etag = c.App.GetPostsEtag(channelId) etag = c.App.GetPostsEtag(channelId, collapsedThreads)
if c.HandleEtag(etag, "Get Posts Before", w, r) { if c.HandleEtag(etag, "Get Posts Before", w, r) {
return return
} }
list, err = c.App.GetPostsBeforePost(model.GetPostsOptions{ChannelId: channelId, PostId: beforePost, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads}) list, err = c.App.GetPostsBeforePost(model.GetPostsOptions{ChannelId: channelId, PostId: beforePost, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, CollapsedThreadsExtended: collapsedThreadsExtended})
} else { } else {
etag = c.App.GetPostsEtag(channelId) etag = c.App.GetPostsEtag(channelId, collapsedThreads)
if c.HandleEtag(etag, "Get Posts", w, r) { if c.HandleEtag(etag, "Get Posts", w, r) {
return return
} }
list, err = c.App.GetPostsPage(model.GetPostsOptions{ChannelId: channelId, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads}) list, err = c.App.GetPostsPage(model.GetPostsOptions{ChannelId: channelId, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, CollapsedThreadsExtended: collapsedThreadsExtended})
} }
if err != nil { if err != nil {
@@ -239,7 +240,10 @@ func getPostsForChannelAroundLastUnread(c *Context, w http.ResponseWriter, r *ht
} }
skipFetchThreads := r.URL.Query().Get("skipFetchThreads") == "true" skipFetchThreads := r.URL.Query().Get("skipFetchThreads") == "true"
postList, err := c.App.GetPostsForChannelAroundLastUnread(channelId, userId, c.Params.LimitBefore, c.Params.LimitAfter, skipFetchThreads) collapsedThreads := r.URL.Query().Get("collapsedThreads") == "true"
collapsedThreadsExtended := r.URL.Query().Get("collapsedThreadsExtended") == "true"
postList, err := c.App.GetPostsForChannelAroundLastUnread(channelId, userId, c.Params.LimitBefore, c.Params.LimitAfter, skipFetchThreads, collapsedThreads, collapsedThreadsExtended)
if err != nil { if err != nil {
c.Err = err c.Err = err
return return
@@ -247,13 +251,13 @@ func getPostsForChannelAroundLastUnread(c *Context, w http.ResponseWriter, r *ht
etag := "" etag := ""
if len(postList.Order) == 0 { if len(postList.Order) == 0 {
etag = c.App.GetPostsEtag(channelId) etag = c.App.GetPostsEtag(channelId, collapsedThreads)
if c.HandleEtag(etag, "Get Posts", w, r) { if c.HandleEtag(etag, "Get Posts", w, r) {
return return
} }
postList, err = c.App.GetPostsPage(model.GetPostsOptions{ChannelId: channelId, Page: app.PageDefault, PerPage: c.Params.LimitBefore, SkipFetchThreads: skipFetchThreads}) postList, err = c.App.GetPostsPage(model.GetPostsOptions{ChannelId: channelId, Page: app.PageDefault, PerPage: c.Params.LimitBefore, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, CollapsedThreadsExtended: collapsedThreadsExtended})
if err != nil { if err != nil {
c.Err = err c.Err = err
return return
@@ -412,7 +416,9 @@ func getPostThread(c *Context, w http.ResponseWriter, r *http.Request) {
return return
} }
skipFetchThreads := r.URL.Query().Get("skipFetchThreads") == "true" skipFetchThreads := r.URL.Query().Get("skipFetchThreads") == "true"
list, err := c.App.GetPostThread(c.Params.PostId, skipFetchThreads) collapsedThreads := r.URL.Query().Get("collapsedThreads") == "true"
collapsedThreadsExtended := r.URL.Query().Get("collapsedThreadsExtended") == "true"
list, err := c.App.GetPostThread(c.Params.PostId, skipFetchThreads, collapsedThreads, collapsedThreadsExtended)
if err != nil { if err != nil {
c.Err = err c.Err = err
return return

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

@@ -389,7 +389,7 @@ func testCreatePostWithOutgoingHook(
if commentPostType { if commentPostType {
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
postList, resp := th.SystemAdminClient.GetPostThread(post.Id, "") postList, resp := th.SystemAdminClient.GetPostThread(post.Id, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Equal(t, post.Id, postList.Order[0], "wrong order") require.Equal(t, post.Id, postList.Order[0], "wrong order")
@@ -1052,17 +1052,17 @@ func TestGetPostsForChannel(t *testing.T) {
post4 := th.CreatePost() post4 := th.CreatePost()
th.TestForAllClients(t, func(t *testing.T, c *model.Client4) { th.TestForAllClients(t, func(t *testing.T, c *model.Client4) {
posts, resp := c.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "") posts, resp := c.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Equal(t, post4.Id, posts.Order[0], "wrong order") require.Equal(t, post4.Id, posts.Order[0], "wrong order")
require.Equal(t, post3.Id, posts.Order[1], "wrong order") require.Equal(t, post3.Id, posts.Order[1], "wrong order")
require.Equal(t, post2.Id, posts.Order[2], "wrong order") require.Equal(t, post2.Id, posts.Order[2], "wrong order")
require.Equal(t, post1.Id, posts.Order[3], "wrong order") require.Equal(t, post1.Id, posts.Order[3], "wrong order")
posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 0, 3, resp.Etag) posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 0, 3, resp.Etag, false)
CheckEtag(t, posts, resp) CheckEtag(t, posts, resp)
posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 0, 3, "") posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 0, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 3, "wrong number returned") require.Len(t, posts.Order, 3, "wrong number returned")
@@ -1071,11 +1071,11 @@ func TestGetPostsForChannel(t *testing.T) {
_, ok = posts.Posts[post1.Id] _, ok = posts.Posts[post1.Id]
require.True(t, ok, "missing root post") require.True(t, ok, "missing root post")
posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 1, 1, "") posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 1, 1, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Equal(t, post3.Id, posts.Order[0], "wrong order") require.Equal(t, post3.Id, posts.Order[0], "wrong order")
posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 10000, 10000, "") posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 10000, 10000, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Empty(t, posts.Order, "should be no posts") require.Empty(t, posts.Order, "should be no posts")
}) })
@@ -1083,7 +1083,7 @@ func TestGetPostsForChannel(t *testing.T) {
post5 := th.CreatePost() post5 := th.CreatePost()
th.TestForAllClients(t, func(t *testing.T, c *model.Client4) { th.TestForAllClients(t, func(t *testing.T, c *model.Client4) {
posts, resp := c.GetPostsSince(th.BasicChannel.Id, since) posts, resp := c.GetPostsSince(th.BasicChannel.Id, since, false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Posts, 2, "should return 2 posts") require.Len(t, posts.Posts, 2, "should return 2 posts")
@@ -1105,18 +1105,18 @@ func TestGetPostsForChannel(t *testing.T) {
require.True(t, f, "missing post") require.True(t, f, "missing post")
} }
_, resp = c.GetPostsForChannel("", 0, 60, "") _, resp = c.GetPostsForChannel("", 0, 60, "", false)
CheckBadRequestStatus(t, resp) CheckBadRequestStatus(t, resp)
_, resp = c.GetPostsForChannel("junk", 0, 60, "") _, resp = c.GetPostsForChannel("junk", 0, 60, "", false)
CheckBadRequestStatus(t, resp) CheckBadRequestStatus(t, resp)
}) })
_, resp := Client.GetPostsForChannel(model.NewId(), 0, 60, "") _, resp := Client.GetPostsForChannel(model.NewId(), 0, 60, "", false)
CheckForbiddenStatus(t, resp) CheckForbiddenStatus(t, resp)
Client.Logout() Client.Logout()
_, resp = Client.GetPostsForChannel(model.NewId(), 0, 60, "") _, resp = Client.GetPostsForChannel(model.NewId(), 0, 60, "", false)
CheckUnauthorizedStatus(t, resp) CheckUnauthorizedStatus(t, resp)
// more tests for next_post_id, prev_post_id, and order // more tests for next_post_id, prev_post_id, and order
@@ -1131,11 +1131,11 @@ func TestGetPostsForChannel(t *testing.T) {
var posts *model.PostList var posts *model.PostList
th.TestForAllClients(t, func(t *testing.T, c *model.Client4) { th.TestForAllClients(t, func(t *testing.T, c *model.Client4) {
// get the system post IDs posted before the created posts above // get the system post IDs posted before the created posts above
posts, resp = c.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "") posts, resp = c.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "", false)
systemPostId1 := posts.Order[1] systemPostId1 := posts.Order[1]
// similar to '/posts' // similar to '/posts'
posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "") posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 12, "expected 12 posts") require.Len(t, posts.Order, 12, "expected 12 posts")
require.Equal(t, post10.Id, posts.Order[0], "posts not in order") require.Equal(t, post10.Id, posts.Order[0], "posts not in order")
@@ -1144,7 +1144,7 @@ func TestGetPostsForChannel(t *testing.T) {
require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId")
// similar to '/posts?per_page=3' // similar to '/posts?per_page=3'
posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 0, 3, "") posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 0, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 3, "expected 3 posts") require.Len(t, posts.Order, 3, "expected 3 posts")
require.Equal(t, post10.Id, posts.Order[0], "posts not in order") require.Equal(t, post10.Id, posts.Order[0], "posts not in order")
@@ -1153,7 +1153,7 @@ func TestGetPostsForChannel(t *testing.T) {
require.Equal(t, post7.Id, posts.PrevPostId, "should return post7.Id as PrevPostId") require.Equal(t, post7.Id, posts.PrevPostId, "should return post7.Id as PrevPostId")
// similar to '/posts?per_page=3&page=1' // similar to '/posts?per_page=3&page=1'
posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 1, 3, "") posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 1, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 3, "expected 3 posts") require.Len(t, posts.Order, 3, "expected 3 posts")
require.Equal(t, post7.Id, posts.Order[0], "posts not in order") require.Equal(t, post7.Id, posts.Order[0], "posts not in order")
@@ -1162,7 +1162,7 @@ func TestGetPostsForChannel(t *testing.T) {
require.Equal(t, post4.Id, posts.PrevPostId, "should return post4.Id as PrevPostId") require.Equal(t, post4.Id, posts.PrevPostId, "should return post4.Id as PrevPostId")
// similar to '/posts?per_page=3&page=2' // similar to '/posts?per_page=3&page=2'
posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 2, 3, "") posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 2, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 3, "expected 3 posts") require.Len(t, posts.Order, 3, "expected 3 posts")
require.Equal(t, post4.Id, posts.Order[0], "posts not in order") require.Equal(t, post4.Id, posts.Order[0], "posts not in order")
@@ -1171,7 +1171,7 @@ func TestGetPostsForChannel(t *testing.T) {
require.Equal(t, post1.Id, posts.PrevPostId, "should return post1.Id as PrevPostId") require.Equal(t, post1.Id, posts.PrevPostId, "should return post1.Id as PrevPostId")
// similar to '/posts?per_page=3&page=3' // similar to '/posts?per_page=3&page=3'
posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 3, 3, "") posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 3, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 3, "expected 3 posts") require.Len(t, posts.Order, 3, "expected 3 posts")
require.Equal(t, post1.Id, posts.Order[0], "posts not in order") require.Equal(t, post1.Id, posts.Order[0], "posts not in order")
@@ -1180,7 +1180,7 @@ func TestGetPostsForChannel(t *testing.T) {
require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId")
// similar to '/posts?per_page=3&page=4' // similar to '/posts?per_page=3&page=4'
posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 4, 3, "") posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 4, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Empty(t, posts.Order, "should return 0 post") require.Empty(t, posts.Order, "should return 0 post")
require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId")
@@ -1390,7 +1390,7 @@ func TestGetPostsBefore(t *testing.T) {
post4 := th.CreatePost() post4 := th.CreatePost()
post5 := th.CreatePost() post5 := th.CreatePost()
posts, resp := Client.GetPostsBefore(th.BasicChannel.Id, post3.Id, 0, 100, "") posts, resp := Client.GetPostsBefore(th.BasicChannel.Id, post3.Id, 0, 100, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
found := make([]bool, 2) found := make([]bool, 2)
@@ -1412,17 +1412,17 @@ func TestGetPostsBefore(t *testing.T) {
require.Equal(t, post3.Id, posts.NextPostId, "should match NextPostId") require.Equal(t, post3.Id, posts.NextPostId, "should match NextPostId")
require.Equal(t, "", posts.PrevPostId, "should match empty PrevPostId") require.Equal(t, "", posts.PrevPostId, "should match empty PrevPostId")
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post4.Id, 1, 1, "") posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post4.Id, 1, 1, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Posts, 1, "too many posts returned") require.Len(t, posts.Posts, 1, "too many posts returned")
require.Equal(t, post2.Id, posts.Order[0], "should match returned post") require.Equal(t, post2.Id, posts.Order[0], "should match returned post")
require.Equal(t, post3.Id, posts.NextPostId, "should match NextPostId") require.Equal(t, post3.Id, posts.NextPostId, "should match NextPostId")
require.Equal(t, post1.Id, posts.PrevPostId, "should match PrevPostId") require.Equal(t, post1.Id, posts.PrevPostId, "should match PrevPostId")
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, "junk", 1, 1, "") posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, "junk", 1, 1, "", false)
CheckBadRequestStatus(t, resp) CheckBadRequestStatus(t, resp)
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post5.Id, 0, 3, "") posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post5.Id, 0, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Posts, 3, "should match length of posts returned") require.Len(t, posts.Posts, 3, "should match length of posts returned")
require.Equal(t, post4.Id, posts.Order[0], "should match returned post") require.Equal(t, post4.Id, posts.Order[0], "should match returned post")
@@ -1431,12 +1431,12 @@ func TestGetPostsBefore(t *testing.T) {
require.Equal(t, post1.Id, posts.PrevPostId, "should match PrevPostId") require.Equal(t, post1.Id, posts.PrevPostId, "should match PrevPostId")
// get the system post IDs posted before the created posts above // get the system post IDs posted before the created posts above
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "") posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
systemPostId2 := posts.Order[0] systemPostId2 := posts.Order[0]
systemPostId1 := posts.Order[1] systemPostId1 := posts.Order[1]
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post5.Id, 1, 3, "") posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post5.Id, 1, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Posts, 3, "should match length of posts returned") require.Len(t, posts.Posts, 3, "should match length of posts returned")
require.Equal(t, post1.Id, posts.Order[0], "should match returned post") require.Equal(t, post1.Id, posts.Order[0], "should match returned post")
@@ -1454,7 +1454,7 @@ func TestGetPostsBefore(t *testing.T) {
th.CreatePost() // post10 th.CreatePost() // post10
// similar to '/posts?before=post9' // similar to '/posts?before=post9'
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 0, 60, "") posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 0, 60, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 10, "expected 10 posts") require.Len(t, posts.Order, 10, "expected 10 posts")
require.Equal(t, post8.Id, posts.Order[0], "posts not in order") require.Equal(t, post8.Id, posts.Order[0], "posts not in order")
@@ -1463,7 +1463,7 @@ func TestGetPostsBefore(t *testing.T) {
require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId")
// similar to '/posts?before=post9&per_page=3' // similar to '/posts?before=post9&per_page=3'
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 0, 3, "") posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 0, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 3, "expected 3 posts") require.Len(t, posts.Order, 3, "expected 3 posts")
require.Equal(t, post8.Id, posts.Order[0], "posts not in order") require.Equal(t, post8.Id, posts.Order[0], "posts not in order")
@@ -1472,7 +1472,7 @@ func TestGetPostsBefore(t *testing.T) {
require.Equal(t, post5.Id, posts.PrevPostId, "should return post5.Id as PrevPostId") require.Equal(t, post5.Id, posts.PrevPostId, "should return post5.Id as PrevPostId")
// similar to '/posts?before=post9&per_page=3&page=1' // similar to '/posts?before=post9&per_page=3&page=1'
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 1, 3, "") posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 1, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 3, "expected 3 posts") require.Len(t, posts.Order, 3, "expected 3 posts")
require.Equal(t, post5.Id, posts.Order[0], "posts not in order") require.Equal(t, post5.Id, posts.Order[0], "posts not in order")
@@ -1481,7 +1481,7 @@ func TestGetPostsBefore(t *testing.T) {
require.Equal(t, post2.Id, posts.PrevPostId, "should return post2.Id as PrevPostId") require.Equal(t, post2.Id, posts.PrevPostId, "should return post2.Id as PrevPostId")
// similar to '/posts?before=post9&per_page=3&page=2' // similar to '/posts?before=post9&per_page=3&page=2'
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 2, 3, "") posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 2, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 3, "expected 3 posts") require.Len(t, posts.Order, 3, "expected 3 posts")
require.Equal(t, post2.Id, posts.Order[0], "posts not in order") require.Equal(t, post2.Id, posts.Order[0], "posts not in order")
@@ -1490,7 +1490,7 @@ func TestGetPostsBefore(t *testing.T) {
require.Equal(t, systemPostId1, posts.PrevPostId, "should return systemPostId1 as PrevPostId") require.Equal(t, systemPostId1, posts.PrevPostId, "should return systemPostId1 as PrevPostId")
// similar to '/posts?before=post1&per_page=3' // similar to '/posts?before=post1&per_page=3'
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 3, "") posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 2, "expected 2 posts") require.Len(t, posts.Order, 2, "expected 2 posts")
require.Equal(t, systemPostId2, posts.Order[0], "posts not in order") require.Equal(t, systemPostId2, posts.Order[0], "posts not in order")
@@ -1499,14 +1499,14 @@ func TestGetPostsBefore(t *testing.T) {
require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId")
// similar to '/posts?before=systemPostId1' // similar to '/posts?before=systemPostId1'
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, systemPostId1, 0, 60, "") posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, systemPostId1, 0, 60, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Empty(t, posts.Order, "should return 0 post") require.Empty(t, posts.Order, "should return 0 post")
require.Equal(t, systemPostId1, posts.NextPostId, "should return systemPostId1 as NextPostId") require.Equal(t, systemPostId1, posts.NextPostId, "should return systemPostId1 as NextPostId")
require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId")
// similar to '/posts?before=systemPostId1&per_page=60&page=1' // similar to '/posts?before=systemPostId1&per_page=60&page=1'
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, systemPostId1, 1, 60, "") posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, systemPostId1, 1, 60, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Empty(t, posts.Order, "should return 0 posts") require.Empty(t, posts.Order, "should return 0 posts")
require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId")
@@ -1514,7 +1514,7 @@ func TestGetPostsBefore(t *testing.T) {
// similar to '/posts?before=non-existent-post' // similar to '/posts?before=non-existent-post'
nonExistentPostId := model.NewId() nonExistentPostId := model.NewId()
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, nonExistentPostId, 0, 60, "") posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, nonExistentPostId, 0, 60, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Empty(t, posts.Order, "should return 0 post") require.Empty(t, posts.Order, "should return 0 post")
require.Equal(t, nonExistentPostId, posts.NextPostId, "should return nonExistentPostId as NextPostId") require.Equal(t, nonExistentPostId, posts.NextPostId, "should return nonExistentPostId as NextPostId")
@@ -1532,7 +1532,7 @@ func TestGetPostsAfter(t *testing.T) {
post4 := th.CreatePost() post4 := th.CreatePost()
post5 := th.CreatePost() post5 := th.CreatePost()
posts, resp := Client.GetPostsAfter(th.BasicChannel.Id, post3.Id, 0, 100, "") posts, resp := Client.GetPostsAfter(th.BasicChannel.Id, post3.Id, 0, 100, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
found := make([]bool, 2) found := make([]bool, 2)
@@ -1552,17 +1552,17 @@ func TestGetPostsAfter(t *testing.T) {
require.Equal(t, "", posts.NextPostId, "should match empty NextPostId") require.Equal(t, "", posts.NextPostId, "should match empty NextPostId")
require.Equal(t, post3.Id, posts.PrevPostId, "should match PrevPostId") require.Equal(t, post3.Id, posts.PrevPostId, "should match PrevPostId")
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 1, 1, "") posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 1, 1, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Posts, 1, "too many posts returned") require.Len(t, posts.Posts, 1, "too many posts returned")
require.Equal(t, post4.Id, posts.Order[0], "should match returned post") require.Equal(t, post4.Id, posts.Order[0], "should match returned post")
require.Equal(t, post5.Id, posts.NextPostId, "should match NextPostId") require.Equal(t, post5.Id, posts.NextPostId, "should match NextPostId")
require.Equal(t, post3.Id, posts.PrevPostId, "should match PrevPostId") require.Equal(t, post3.Id, posts.PrevPostId, "should match PrevPostId")
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, "junk", 1, 1, "") posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, "junk", 1, 1, "", false)
CheckBadRequestStatus(t, resp) CheckBadRequestStatus(t, resp)
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post1.Id, 0, 3, "") posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post1.Id, 0, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Posts, 3, "should match length of posts returned") require.Len(t, posts.Posts, 3, "should match length of posts returned")
require.Equal(t, post4.Id, posts.Order[0], "should match returned post") require.Equal(t, post4.Id, posts.Order[0], "should match returned post")
@@ -1570,7 +1570,7 @@ func TestGetPostsAfter(t *testing.T) {
require.Equal(t, post5.Id, posts.NextPostId, "should match NextPostId") require.Equal(t, post5.Id, posts.NextPostId, "should match NextPostId")
require.Equal(t, post1.Id, posts.PrevPostId, "should match PrevPostId") require.Equal(t, post1.Id, posts.PrevPostId, "should match PrevPostId")
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post1.Id, 1, 3, "") posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post1.Id, 1, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Posts, 1, "should match length of posts returned") require.Len(t, posts.Posts, 1, "should match length of posts returned")
require.Equal(t, post5.Id, posts.Order[0], "should match returned post") require.Equal(t, post5.Id, posts.Order[0], "should match returned post")
@@ -1586,7 +1586,7 @@ func TestGetPostsAfter(t *testing.T) {
post10 := th.CreatePost() post10 := th.CreatePost()
// similar to '/posts?after=post2' // similar to '/posts?after=post2'
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 0, 60, "") posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 0, 60, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 8, "expected 8 posts") require.Len(t, posts.Order, 8, "expected 8 posts")
require.Equal(t, post10.Id, posts.Order[0], "should match order") require.Equal(t, post10.Id, posts.Order[0], "should match order")
@@ -1595,7 +1595,7 @@ func TestGetPostsAfter(t *testing.T) {
require.Equal(t, post2.Id, posts.PrevPostId, "should return post2.Id as PrevPostId") require.Equal(t, post2.Id, posts.PrevPostId, "should return post2.Id as PrevPostId")
// similar to '/posts?after=post2&per_page=3' // similar to '/posts?after=post2&per_page=3'
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 0, 3, "") posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 0, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 3, "expected 3 posts") require.Len(t, posts.Order, 3, "expected 3 posts")
require.Equal(t, post5.Id, posts.Order[0], "should match order") require.Equal(t, post5.Id, posts.Order[0], "should match order")
@@ -1604,7 +1604,7 @@ func TestGetPostsAfter(t *testing.T) {
require.Equal(t, post2.Id, posts.PrevPostId, "should return post2.Id as PrevPostId") require.Equal(t, post2.Id, posts.PrevPostId, "should return post2.Id as PrevPostId")
// similar to '/posts?after=post2&per_page=3&page=1' // similar to '/posts?after=post2&per_page=3&page=1'
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 1, 3, "") posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 1, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 3, "expected 3 posts") require.Len(t, posts.Order, 3, "expected 3 posts")
require.Equal(t, post8.Id, posts.Order[0], "should match order") require.Equal(t, post8.Id, posts.Order[0], "should match order")
@@ -1613,7 +1613,7 @@ func TestGetPostsAfter(t *testing.T) {
require.Equal(t, post5.Id, posts.PrevPostId, "should return post5.Id as PrevPostId") require.Equal(t, post5.Id, posts.PrevPostId, "should return post5.Id as PrevPostId")
// similar to '/posts?after=post2&per_page=3&page=2' // similar to '/posts?after=post2&per_page=3&page=2'
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 2, 3, "") posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 2, 3, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 2, "expected 2 posts") require.Len(t, posts.Order, 2, "expected 2 posts")
require.Equal(t, post10.Id, posts.Order[0], "should match order") require.Equal(t, post10.Id, posts.Order[0], "should match order")
@@ -1622,14 +1622,14 @@ func TestGetPostsAfter(t *testing.T) {
require.Equal(t, post8.Id, posts.PrevPostId, "should return post8.Id as PrevPostId") require.Equal(t, post8.Id, posts.PrevPostId, "should return post8.Id as PrevPostId")
// similar to '/posts?after=post10' // similar to '/posts?after=post10'
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post10.Id, 0, 60, "") posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post10.Id, 0, 60, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Empty(t, posts.Order, "should return 0 post") require.Empty(t, posts.Order, "should return 0 post")
require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId")
require.Equal(t, post10.Id, posts.PrevPostId, "should return post10.Id as PrevPostId") require.Equal(t, post10.Id, posts.PrevPostId, "should return post10.Id as PrevPostId")
// similar to '/posts?after=post10&page=1' // similar to '/posts?after=post10&page=1'
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post10.Id, 1, 60, "") posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post10.Id, 1, 60, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Empty(t, posts.Order, "should return 0 post") require.Empty(t, posts.Order, "should return 0 post")
require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId")
@@ -1637,7 +1637,7 @@ func TestGetPostsAfter(t *testing.T) {
// similar to '/posts?after=non-existent-post' // similar to '/posts?after=non-existent-post'
nonExistentPostId := model.NewId() nonExistentPostId := model.NewId()
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, nonExistentPostId, 0, 60, "") posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, nonExistentPostId, 0, 60, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Empty(t, posts.Order, "should return 0 post") require.Empty(t, posts.Order, "should return 0 post")
require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId")
@@ -1720,13 +1720,13 @@ func TestGetPostsForChannelAroundLastUnread(t *testing.T) {
} }
// Setting limit_after to zero should fail with a 400 BadRequest. // Setting limit_after to zero should fail with a 400 BadRequest.
posts, resp := Client.GetPostsAroundLastUnread(userId, channelId, 20, 0) posts, resp := Client.GetPostsAroundLastUnread(userId, channelId, 20, 0, false)
require.Error(t, resp.Error) require.Error(t, resp.Error)
require.Equal(t, "api.context.invalid_url_param.app_error", resp.Error.Id) require.Equal(t, "api.context.invalid_url_param.app_error", resp.Error.Id)
require.Equal(t, http.StatusBadRequest, resp.StatusCode) require.Equal(t, http.StatusBadRequest, resp.StatusCode)
// All returned posts are all read by the user, since it's created by the user itself. // All returned posts are all read by the user, since it's created by the user itself.
posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 20, 20) posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 20, 20, false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 12, "Should return 12 posts only since there's no unread post") require.Len(t, posts.Order, 12, "Should return 12 posts only since there's no unread post")
@@ -1739,13 +1739,13 @@ func TestGetPostsForChannelAroundLastUnread(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId) th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId)
posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 20, 20) posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 20, 20, false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Len(t, posts.Order, 12, "Should return 12 posts only since there's no unread post") require.Len(t, posts.Order, 12, "Should return 12 posts only since there's no unread post")
// get the first system post generated before the created posts above // get the first system post generated before the created posts above
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "") posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
systemPost0 := posts.Posts[posts.Order[0]] systemPost0 := posts.Posts[posts.Order[0]]
postIdNames[systemPost0.Id] = "system post 0" postIdNames[systemPost0.Id] = "system post 0"
@@ -1760,7 +1760,7 @@ func TestGetPostsForChannelAroundLastUnread(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId) th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId)
posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 3, 3) posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 3, 3, false)
CheckNoError(t, resp) CheckNoError(t, resp)
assertPostList(t, &model.PostList{ assertPostList(t, &model.PostList{
@@ -1784,7 +1784,7 @@ func TestGetPostsForChannelAroundLastUnread(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId) th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId)
posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 3, 3) posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 3, 3, false)
CheckNoError(t, resp) CheckNoError(t, resp)
assertPostList(t, &model.PostList{ assertPostList(t, &model.PostList{
@@ -1811,7 +1811,7 @@ func TestGetPostsForChannelAroundLastUnread(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId) th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId)
posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 3, 3) posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 3, 3, false)
CheckNoError(t, resp) CheckNoError(t, resp)
assertPostList(t, &model.PostList{ assertPostList(t, &model.PostList{
@@ -1836,7 +1836,7 @@ func TestGetPostsForChannelAroundLastUnread(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId) th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId)
posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 3, 3) posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 3, 3, false)
CheckNoError(t, resp) CheckNoError(t, resp)
assertPostList(t, &model.PostList{ assertPostList(t, &model.PostList{
@@ -1876,7 +1876,7 @@ func TestGetPostsForChannelAroundLastUnread(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId) th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId)
posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 1, 2) posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 1, 2, false)
CheckNoError(t, resp) CheckNoError(t, resp)
assertPostList(t, &model.PostList{ assertPostList(t, &model.PostList{
@@ -2051,11 +2051,11 @@ func TestGetPostThread(t *testing.T) {
post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", RootId: th.BasicPost.Id} post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", RootId: th.BasicPost.Id}
post, _ = Client.CreatePost(post) post, _ = Client.CreatePost(post)
list, resp := Client.GetPostThread(th.BasicPost.Id, "") list, resp := Client.GetPostThread(th.BasicPost.Id, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
var list2 *model.PostList var list2 *model.PostList
list2, resp = Client.GetPostThread(th.BasicPost.Id, resp.Etag) list2, resp = Client.GetPostThread(th.BasicPost.Id, resp.Etag, false)
CheckEtag(t, list2, resp) CheckEtag(t, list2, resp)
require.Equal(t, th.BasicPost.Id, list.Order[0], "wrong order") require.Equal(t, th.BasicPost.Id, list.Order[0], "wrong order")
@@ -2065,34 +2065,34 @@ func TestGetPostThread(t *testing.T) {
_, ok = list.Posts[post.Id] _, ok = list.Posts[post.Id]
require.True(t, ok, "should have had post") require.True(t, ok, "should have had post")
_, resp = Client.GetPostThread("junk", "") _, resp = Client.GetPostThread("junk", "", false)
CheckBadRequestStatus(t, resp) CheckBadRequestStatus(t, resp)
_, resp = Client.GetPostThread(model.NewId(), "") _, resp = Client.GetPostThread(model.NewId(), "", false)
CheckNotFoundStatus(t, resp) CheckNotFoundStatus(t, resp)
Client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser.Id) Client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser.Id)
// Channel is public, should be able to read post // Channel is public, should be able to read post
_, resp = Client.GetPostThread(th.BasicPost.Id, "") _, resp = Client.GetPostThread(th.BasicPost.Id, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
privatePost := th.CreatePostWithClient(Client, th.BasicPrivateChannel) privatePost := th.CreatePostWithClient(Client, th.BasicPrivateChannel)
_, resp = Client.GetPostThread(privatePost.Id, "") _, resp = Client.GetPostThread(privatePost.Id, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
Client.RemoveUserFromChannel(th.BasicPrivateChannel.Id, th.BasicUser.Id) Client.RemoveUserFromChannel(th.BasicPrivateChannel.Id, th.BasicUser.Id)
// Channel is private, should not be able to read post // Channel is private, should not be able to read post
_, resp = Client.GetPostThread(privatePost.Id, "") _, resp = Client.GetPostThread(privatePost.Id, "", false)
CheckForbiddenStatus(t, resp) CheckForbiddenStatus(t, resp)
Client.Logout() Client.Logout()
_, resp = Client.GetPostThread(model.NewId(), "") _, resp = Client.GetPostThread(model.NewId(), "", false)
CheckUnauthorizedStatus(t, resp) CheckUnauthorizedStatus(t, resp)
_, resp = th.SystemAdminClient.GetPostThread(th.BasicPost.Id, "") _, resp = th.SystemAdminClient.GetPostThread(th.BasicPost.Id, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
} }

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

@@ -2704,7 +2704,7 @@ func TestImportTeam(t *testing.T) {
CheckNoError(t, resp) CheckNoError(t, resp)
require.Equal(t, importedChannel.Name, "general", "names did not match expected: general") require.Equal(t, importedChannel.Name, "general", "names did not match expected: general")
posts, resp := th.SystemAdminClient.GetPostsForChannel(importedChannel.Id, 0, 60, "") posts, resp := th.SystemAdminClient.GetPostsForChannel(importedChannel.Id, 0, 60, "", false)
CheckNoError(t, resp) CheckNoError(t, resp)
require.Equal(t, posts.Posts[posts.Order[3]].Message, "This is a test post to test the import process", "missing posts in the import process") require.Equal(t, posts.Posts[posts.Order[3]].Message, "This is a test post to test the import process", "missing posts in the import process")
}) })

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

@@ -631,13 +631,13 @@ type AppIface interface {
GetPostAfterTime(channelId string, time int64) (*model.Post, *model.AppError) GetPostAfterTime(channelId string, time int64) (*model.Post, *model.AppError)
GetPostIdAfterTime(channelId string, time int64) (string, *model.AppError) GetPostIdAfterTime(channelId string, time int64) (string, *model.AppError)
GetPostIdBeforeTime(channelId string, time int64) (string, *model.AppError) GetPostIdBeforeTime(channelId string, time int64) (string, *model.AppError)
GetPostThread(postId string, skipFetchThreads bool) (*model.PostList, *model.AppError) GetPostThread(postId string, skipFetchThreads, collapsedThreads, collapsedThreadsExtended bool) (*model.PostList, *model.AppError)
GetPosts(channelId string, offset int, limit int) (*model.PostList, *model.AppError) GetPosts(channelId string, offset int, limit int) (*model.PostList, *model.AppError)
GetPostsAfterPost(options model.GetPostsOptions) (*model.PostList, *model.AppError) GetPostsAfterPost(options model.GetPostsOptions) (*model.PostList, *model.AppError)
GetPostsAroundPost(before bool, options model.GetPostsOptions) (*model.PostList, *model.AppError) GetPostsAroundPost(before bool, options model.GetPostsOptions) (*model.PostList, *model.AppError)
GetPostsBeforePost(options model.GetPostsOptions) (*model.PostList, *model.AppError) GetPostsBeforePost(options model.GetPostsOptions) (*model.PostList, *model.AppError)
GetPostsEtag(channelId string) string GetPostsEtag(channelId string, collapsedThreads bool) string
GetPostsForChannelAroundLastUnread(channelId, userId string, limitBefore, limitAfter int, skipFetchThreads bool) (*model.PostList, *model.AppError) GetPostsForChannelAroundLastUnread(channelId, userId string, limitBefore, limitAfter int, skipFetchThreads bool, collapsedThreads, collapsedThreadsExtended bool) (*model.PostList, *model.AppError)
GetPostsPage(options model.GetPostsOptions) (*model.PostList, *model.AppError) GetPostsPage(options model.GetPostsOptions) (*model.PostList, *model.AppError)
GetPostsSince(options model.GetPostsSinceOptions) (*model.PostList, *model.AppError) GetPostsSince(options model.GetPostsSinceOptions) (*model.PostList, *model.AppError)
GetPreferenceByCategoryAndNameForUser(userId string, category string, preferenceName string) (*model.Preference, *model.AppError) GetPreferenceByCategoryAndNameForUser(userId string, category string, preferenceName string) (*model.Preference, *model.AppError)

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

@@ -388,7 +388,7 @@ func (a *App) MigrateFilenamesToFileInfos(post *model.Post) []*model.FileInfo {
fileMigrationLock.Lock() fileMigrationLock.Lock()
defer fileMigrationLock.Unlock() defer fileMigrationLock.Unlock()
result, nErr := a.Srv().Store.Post().Get(post.Id, false) result, nErr := a.Srv().Store.Post().Get(post.Id, false, false, false)
if nErr != nil { if nErr != nil {
mlog.Error("Unable to get post when migrating post to use FileInfos", mlog.Err(nErr), mlog.String("post_id", post.Id)) mlog.Error("Unable to get post when migrating post to use FileInfos", mlog.Err(nErr), mlog.String("post_id", post.Id))
return []*model.FileInfo{} return []*model.FileInfo{}

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

@@ -7056,7 +7056,7 @@ func (a *OpenTracingAppLayer) GetPostIdBeforeTime(channelId string, time int64)
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (a *OpenTracingAppLayer) GetPostThread(postId string, skipFetchThreads bool) (*model.PostList, *model.AppError) { func (a *OpenTracingAppLayer) GetPostThread(postId string, skipFetchThreads bool, collapsedThreads bool, collapsedThreadsExtended bool) (*model.PostList, *model.AppError) {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetPostThread") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetPostThread")
@@ -7068,7 +7068,7 @@ func (a *OpenTracingAppLayer) GetPostThread(postId string, skipFetchThreads bool
}() }()
defer span.Finish() defer span.Finish()
resultVar0, resultVar1 := a.app.GetPostThread(postId, skipFetchThreads) resultVar0, resultVar1 := a.app.GetPostThread(postId, skipFetchThreads, collapsedThreads, collapsedThreadsExtended)
if resultVar1 != nil { if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1)) span.LogFields(spanlog.Error(resultVar1))
@@ -7166,7 +7166,7 @@ func (a *OpenTracingAppLayer) GetPostsBeforePost(options model.GetPostsOptions)
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (a *OpenTracingAppLayer) GetPostsEtag(channelId string) string { func (a *OpenTracingAppLayer) GetPostsEtag(channelId string, collapsedThreads bool) string {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetPostsEtag") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetPostsEtag")
@@ -7178,12 +7178,12 @@ func (a *OpenTracingAppLayer) GetPostsEtag(channelId string) string {
}() }()
defer span.Finish() defer span.Finish()
resultVar0 := a.app.GetPostsEtag(channelId) resultVar0 := a.app.GetPostsEtag(channelId, collapsedThreads)
return resultVar0 return resultVar0
} }
func (a *OpenTracingAppLayer) GetPostsForChannelAroundLastUnread(channelId string, userId string, limitBefore int, limitAfter int, skipFetchThreads bool) (*model.PostList, *model.AppError) { func (a *OpenTracingAppLayer) GetPostsForChannelAroundLastUnread(channelId string, userId string, limitBefore int, limitAfter int, skipFetchThreads bool, collapsedThreads bool, collapsedThreadsExtended bool) (*model.PostList, *model.AppError) {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetPostsForChannelAroundLastUnread") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetPostsForChannelAroundLastUnread")
@@ -7195,7 +7195,7 @@ func (a *OpenTracingAppLayer) GetPostsForChannelAroundLastUnread(channelId strin
}() }()
defer span.Finish() defer span.Finish()
resultVar0, resultVar1 := a.app.GetPostsForChannelAroundLastUnread(channelId, userId, limitBefore, limitAfter, skipFetchThreads) resultVar0, resultVar1 := a.app.GetPostsForChannelAroundLastUnread(channelId, userId, limitBefore, limitAfter, skipFetchThreads, collapsedThreads, collapsedThreadsExtended)
if resultVar1 != nil { if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1)) span.LogFields(spanlog.Error(resultVar1))

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

@@ -574,7 +574,7 @@ func (api *PluginAPI) DeletePost(postId string) *model.AppError {
} }
func (api *PluginAPI) GetPostThread(postId string) (*model.PostList, *model.AppError) { func (api *PluginAPI) GetPostThread(postId string) (*model.PostList, *model.AppError) {
return api.app.GetPostThread(postId, false) return api.app.GetPostThread(postId, false, false, false)
} }
func (api *PluginAPI) GetPost(postId string) (*model.Post, *model.AppError) { func (api *PluginAPI) GetPost(postId string) (*model.Post, *model.AppError) {

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

@@ -185,7 +185,7 @@ func (a *App) CreatePost(post *model.Post, channel *model.Channel, triggerWebhoo
if len(post.RootId) > 0 { if len(post.RootId) > 0 {
pchan = make(chan store.StoreResult, 1) pchan = make(chan store.StoreResult, 1)
go func() { go func() {
r, pErr := a.Srv().Store.Post().Get(post.RootId, false) r, pErr := a.Srv().Store.Post().Get(post.RootId, false, false, false)
pchan <- store.StoreResult{Data: r, NErr: pErr} pchan <- store.StoreResult{Data: r, NErr: pErr}
close(pchan) close(pchan)
}() }()
@@ -542,7 +542,7 @@ func (a *App) DeleteEphemeralPost(userId, postId string) {
func (a *App) UpdatePost(post *model.Post, safeUpdate bool) (*model.Post, *model.AppError) { func (a *App) UpdatePost(post *model.Post, safeUpdate bool) (*model.Post, *model.AppError) {
post.SanitizeProps() post.SanitizeProps()
postLists, nErr := a.Srv().Store.Post().Get(post.Id, false) postLists, nErr := a.Srv().Store.Post().Get(post.Id, false, false, false)
if nErr != nil { if nErr != nil {
var nfErr *store.ErrNotFound var nfErr *store.ErrNotFound
var invErr *store.ErrInvalidInput var invErr *store.ErrInvalidInput
@@ -718,8 +718,8 @@ func (a *App) GetPosts(channelId string, offset int, limit int) (*model.PostList
return postList, nil return postList, nil
} }
func (a *App) GetPostsEtag(channelId string) string { func (a *App) GetPostsEtag(channelId string, collapsedThreads bool) string {
return a.Srv().Store.Post().GetEtag(channelId, true) return a.Srv().Store.Post().GetEtag(channelId, true, collapsedThreads)
} }
func (a *App) GetPostsSince(options model.GetPostsSinceOptions) (*model.PostList, *model.AppError) { func (a *App) GetPostsSince(options model.GetPostsSinceOptions) (*model.PostList, *model.AppError) {
@@ -746,8 +746,8 @@ func (a *App) GetSinglePost(postId string) (*model.Post, *model.AppError) {
return post, nil return post, nil
} }
func (a *App) GetPostThread(postId string, skipFetchThreads bool) (*model.PostList, *model.AppError) { func (a *App) GetPostThread(postId string, skipFetchThreads, collapsedThreads, collapsedThreadsExtended bool) (*model.PostList, *model.AppError) {
posts, err := a.Srv().Store.Post().Get(postId, skipFetchThreads) posts, err := a.Srv().Store.Post().Get(postId, skipFetchThreads, collapsedThreads, collapsedThreadsExtended)
if err != nil { if err != nil {
var nfErr *store.ErrNotFound var nfErr *store.ErrNotFound
var invErr *store.ErrInvalidInput var invErr *store.ErrInvalidInput
@@ -792,7 +792,7 @@ func (a *App) GetFlaggedPostsForChannel(userId, channelId string, offset int, li
} }
func (a *App) GetPermalinkPost(postId string, userId string) (*model.PostList, *model.AppError) { func (a *App) GetPermalinkPost(postId string, userId string) (*model.PostList, *model.AppError) {
list, nErr := a.Srv().Store.Post().Get(postId, false) list, nErr := a.Srv().Store.Post().Get(postId, false, false, false)
if nErr != nil { if nErr != nil {
var nfErr *store.ErrNotFound var nfErr *store.ErrNotFound
var invErr *store.ErrInvalidInput var invErr *store.ErrInvalidInput
@@ -975,7 +975,7 @@ func (a *App) AddCursorIdsForPostList(originalList *model.PostList, afterPost, b
originalList.NextPostId = nextPostId originalList.NextPostId = nextPostId
originalList.PrevPostId = prevPostId originalList.PrevPostId = prevPostId
} }
func (a *App) GetPostsForChannelAroundLastUnread(channelId, userId string, limitBefore, limitAfter int, skipFetchThreads bool) (*model.PostList, *model.AppError) { func (a *App) GetPostsForChannelAroundLastUnread(channelId, userId string, limitBefore, limitAfter int, skipFetchThreads bool, collapsedThreads, collapsedThreadsExtended bool) (*model.PostList, *model.AppError) {
var member *model.ChannelMember var member *model.ChannelMember
var err *model.AppError var err *model.AppError
if member, err = a.GetChannelMember(channelId, userId); err != nil { if member, err = a.GetChannelMember(channelId, userId); err != nil {
@@ -991,7 +991,7 @@ func (a *App) GetPostsForChannelAroundLastUnread(channelId, userId string, limit
return model.NewPostList(), nil return model.NewPostList(), nil
} }
postList, err := a.GetPostThread(lastUnreadPostId, skipFetchThreads) postList, err := a.GetPostThread(lastUnreadPostId, skipFetchThreads, collapsedThreads, collapsedThreadsExtended)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -999,13 +999,13 @@ func (a *App) GetPostsForChannelAroundLastUnread(channelId, userId string, limit
// channel organically, those replies will be added below. // channel organically, those replies will be added below.
postList.Order = []string{lastUnreadPostId} postList.Order = []string{lastUnreadPostId}
if postListBefore, err := a.GetPostsBeforePost(model.GetPostsOptions{ChannelId: channelId, PostId: lastUnreadPostId, Page: PageDefault, PerPage: limitBefore, SkipFetchThreads: skipFetchThreads}); err != nil { if postListBefore, err := a.GetPostsBeforePost(model.GetPostsOptions{ChannelId: channelId, PostId: lastUnreadPostId, Page: PageDefault, PerPage: limitBefore, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, CollapsedThreadsExtended: collapsedThreadsExtended}); err != nil {
return nil, err return nil, err
} else if postListBefore != nil { } else if postListBefore != nil {
postList.Extend(postListBefore) postList.Extend(postListBefore)
} }
if postListAfter, err := a.GetPostsAfterPost(model.GetPostsOptions{ChannelId: channelId, PostId: lastUnreadPostId, Page: PageDefault, PerPage: limitAfter - 1, SkipFetchThreads: skipFetchThreads}); err != nil { if postListAfter, err := a.GetPostsAfterPost(model.GetPostsOptions{ChannelId: channelId, PostId: lastUnreadPostId, Page: PageDefault, PerPage: limitAfter - 1, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, CollapsedThreadsExtended: collapsedThreadsExtended}); err != nil {
return nil, err return nil, err
} else if postListAfter != nil { } else if postListAfter != nil {
postList.Extend(postListAfter) postList.Extend(postListAfter)
@@ -1434,7 +1434,7 @@ func (a *App) countMentionsFromPost(user *model.User, post *model.Post) (int, *m
// A mapping of thread root IDs to whether or not a post in that thread mentions the user // A mapping of thread root IDs to whether or not a post in that thread mentions the user
mentionedByThread := make(map[string]bool) mentionedByThread := make(map[string]bool)
thread, err := a.GetPostThread(post.Id, false) thread, err := a.GetPostThread(post.Id, false, false, false)
if err != nil { if err != nil {
return 0, err return 0, err
} }

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

@@ -1921,3 +1921,52 @@ func TestThreadMembership(t *testing.T) {
require.Len(t, memberships, 2) require.Len(t, memberships, 2)
}) })
} }
func TestCollapsedThreadFetch(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ThreadAutoFollow = true
*cfg.ServiceSettings.CollapsedThreads = model.COLLAPSED_THREADS_DEFAULT_ON
})
user1 := th.BasicUser
user2 := th.BasicUser2
t.Run("should only return root posts, enriched", func(t *testing.T) {
channel := th.CreateChannel(th.BasicTeam)
th.AddUserToChannel(user2, channel)
defer th.App.DeleteChannel(channel, user1.Id)
postRoot, err := th.App.CreatePost(&model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "root post",
}, channel, false, true)
require.Nil(t, err)
_, err = th.App.CreatePost(&model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
RootId: postRoot.Id,
Message: fmt.Sprintf("@%s", user2.Username),
}, channel, false, true)
require.Nil(t, err)
thread, nErr := th.App.Srv().Store.Thread().Get(postRoot.Id)
require.Nil(t, nErr)
require.Len(t, thread.Participants, 2)
th.App.MarkChannelAsUnreadFromPost(postRoot.Id, user1.Id)
l, err := th.App.GetPostsForChannelAroundLastUnread(channel.Id, user1.Id, 10, 10, true, true, false)
require.Nil(t, err)
require.Len(t, l.Order, 1)
require.EqualValues(t, 1, l.Posts[postRoot.Id].ReplyCount)
require.EqualValues(t, []string{user1.Id, user2.Id}, []string{l.Posts[postRoot.Id].Participants[0].Id, l.Posts[postRoot.Id].Participants[1].Id})
require.Empty(t, l.Posts[postRoot.Id].Participants[0].Email)
require.NotZero(t, l.Posts[postRoot.Id].LastReplyAt)
// try extended fetch
l, err = th.App.GetPostsForChannelAroundLastUnread(channel.Id, user1.Id, 10, 10, true, true, true)
require.Nil(t, err)
require.Len(t, l.Order, 1)
require.NotEmpty(t, l.Posts[postRoot.Id].Participants[0].Email)
})
}

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

@@ -135,6 +135,7 @@ func GenerateClientConfig(c *model.Config, telemetryID string, license *model.Li
props["CustomUrlSchemes"] = strings.Join(c.DisplaySettings.CustomUrlSchemes, ",") props["CustomUrlSchemes"] = strings.Join(c.DisplaySettings.CustomUrlSchemes, ",")
props["IsDefaultMarketplace"] = strconv.FormatBool(*c.PluginSettings.MarketplaceUrl == model.PLUGIN_SETTINGS_DEFAULT_MARKETPLACE_URL) props["IsDefaultMarketplace"] = strconv.FormatBool(*c.PluginSettings.MarketplaceUrl == model.PLUGIN_SETTINGS_DEFAULT_MARKETPLACE_URL)
props["ExperimentalSharedChannels"] = "false" props["ExperimentalSharedChannels"] = "false"
props["CollapsedThreads"] = *c.ServiceSettings.CollapsedThreads
if license != nil { if license != nil {
props["ExperimentalHideTownSquareinLHS"] = strconv.FormatBool(*c.TeamSettings.ExperimentalHideTownSquareinLHS) props["ExperimentalHideTownSquareinLHS"] = strconv.FormatBool(*c.TeamSettings.ExperimentalHideTownSquareinLHS)

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

@@ -2903,8 +2903,12 @@ func (c *Client4) DeletePost(postId string) (bool, *Response) {
} }
// GetPostThread gets a post with all the other posts in the same thread. // GetPostThread gets a post with all the other posts in the same thread.
func (c *Client4) GetPostThread(postId string, etag string) (*PostList, *Response) { func (c *Client4) GetPostThread(postId string, etag string, collapsedThreads bool) (*PostList, *Response) {
r, err := c.DoApiGet(c.GetPostRoute(postId)+"/thread", etag) url := c.GetPostRoute(postId) + "/thread"
if collapsedThreads {
url += "?collapsedThreads=true"
}
r, err := c.DoApiGet(url, etag)
if err != nil { if err != nil {
return nil, BuildErrorResponse(r, err) return nil, BuildErrorResponse(r, err)
} }
@@ -2913,8 +2917,11 @@ func (c *Client4) GetPostThread(postId string, etag string) (*PostList, *Respons
} }
// GetPostsForChannel gets a page of posts with an array for ordering for a channel. // GetPostsForChannel gets a page of posts with an array for ordering for a channel.
func (c *Client4) GetPostsForChannel(channelId string, page, perPage int, etag string) (*PostList, *Response) { func (c *Client4) GetPostsForChannel(channelId string, page, perPage int, etag string, collapsedThreads bool) (*PostList, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
if collapsedThreads {
query += "&collapsedThreads=true"
}
r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag) r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag)
if err != nil { if err != nil {
return nil, BuildErrorResponse(r, err) return nil, BuildErrorResponse(r, err)
@@ -2965,8 +2972,11 @@ func (c *Client4) GetFlaggedPostsForUserInChannel(userId string, channelId strin
} }
// GetPostsSince gets posts created after a specified time as Unix time in milliseconds. // GetPostsSince gets posts created after a specified time as Unix time in milliseconds.
func (c *Client4) GetPostsSince(channelId string, time int64) (*PostList, *Response) { func (c *Client4) GetPostsSince(channelId string, time int64, collapsedThreads bool) (*PostList, *Response) {
query := fmt.Sprintf("?since=%v", time) query := fmt.Sprintf("?since=%v", time)
if collapsedThreads {
query += "&collapsedThreads=true"
}
r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, "") r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, "")
if err != nil { if err != nil {
return nil, BuildErrorResponse(r, err) return nil, BuildErrorResponse(r, err)
@@ -2976,8 +2986,11 @@ func (c *Client4) GetPostsSince(channelId string, time int64) (*PostList, *Respo
} }
// GetPostsAfter gets a page of posts that were posted after the post provided. // GetPostsAfter gets a page of posts that were posted after the post provided.
func (c *Client4) GetPostsAfter(channelId, postId string, page, perPage int, etag string) (*PostList, *Response) { func (c *Client4) GetPostsAfter(channelId, postId string, page, perPage int, etag string, collapsedThreads bool) (*PostList, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v&after=%v", page, perPage, postId) query := fmt.Sprintf("?page=%v&per_page=%v&after=%v", page, perPage, postId)
if collapsedThreads {
query += "&collapsedThreads=true"
}
r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag) r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag)
if err != nil { if err != nil {
return nil, BuildErrorResponse(r, err) return nil, BuildErrorResponse(r, err)
@@ -2987,8 +3000,11 @@ func (c *Client4) GetPostsAfter(channelId, postId string, page, perPage int, eta
} }
// GetPostsBefore gets a page of posts that were posted before the post provided. // GetPostsBefore gets a page of posts that were posted before the post provided.
func (c *Client4) GetPostsBefore(channelId, postId string, page, perPage int, etag string) (*PostList, *Response) { func (c *Client4) GetPostsBefore(channelId, postId string, page, perPage int, etag string, collapsedThreads bool) (*PostList, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v&before=%v", page, perPage, postId) query := fmt.Sprintf("?page=%v&per_page=%v&before=%v", page, perPage, postId)
if collapsedThreads {
query += "&collapsedThreads=true"
}
r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag) r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag)
if err != nil { if err != nil {
return nil, BuildErrorResponse(r, err) return nil, BuildErrorResponse(r, err)
@@ -2998,8 +3014,11 @@ func (c *Client4) GetPostsBefore(channelId, postId string, page, perPage int, et
} }
// GetPostsAroundLastUnread gets a list of posts around last unread post by a user in a channel. // GetPostsAroundLastUnread gets a list of posts around last unread post by a user in a channel.
func (c *Client4) GetPostsAroundLastUnread(userId, channelId string, limitBefore, limitAfter int) (*PostList, *Response) { func (c *Client4) GetPostsAroundLastUnread(userId, channelId string, limitBefore, limitAfter int, collapsedThreads bool) (*PostList, *Response) {
query := fmt.Sprintf("?limit_before=%v&limit_after=%v", limitBefore, limitAfter) query := fmt.Sprintf("?limit_before=%v&limit_after=%v", limitBefore, limitAfter)
if collapsedThreads {
query += "&collapsedThreads=true"
}
r, err := c.DoApiGet(c.GetUserRoute(userId)+c.GetChannelRoute(channelId)+"/posts/unread"+query, "") r, err := c.DoApiGet(c.GetUserRoute(userId)+c.GetChannelRoute(channelId)+"/posts/unread"+query, "")
if err != nil { if err != nil {
return nil, BuildErrorResponse(r, err) return nil, BuildErrorResponse(r, err)

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

@@ -99,8 +99,10 @@ type Post struct {
HasReactions bool `json:"has_reactions,omitempty"` HasReactions bool `json:"has_reactions,omitempty"`
// Transient data populated before sending a post to the client // Transient data populated before sending a post to the client
ReplyCount int64 `json:"reply_count" db:"-"` ReplyCount int64 `json:"reply_count" db:"-"`
Metadata *PostMetadata `json:"metadata,omitempty" db:"-"` LastReplyAt int64 `json:"last_reply_at" db:"-"`
Participants []*User `json:"participants" db:"-"`
Metadata *PostMetadata `json:"metadata,omitempty" db:"-"`
} }
type PostEphemeral struct { type PostEphemeral struct {
@@ -201,6 +203,8 @@ func (o *Post) ShallowCopy(dst *Post) error {
dst.PendingPostId = o.PendingPostId dst.PendingPostId = o.PendingPostId
dst.HasReactions = o.HasReactions dst.HasReactions = o.HasReactions
dst.ReplyCount = o.ReplyCount dst.ReplyCount = o.ReplyCount
dst.Participants = o.Participants
dst.LastReplyAt = o.LastReplyAt
dst.Metadata = o.Metadata dst.Metadata = o.Metadata
return nil return nil
} }
@@ -225,17 +229,21 @@ func (o *Post) ToUnsanitizedJson() string {
} }
type GetPostsSinceOptions struct { type GetPostsSinceOptions struct {
ChannelId string ChannelId string
Time int64 Time int64
SkipFetchThreads bool SkipFetchThreads bool
CollapsedThreads bool
CollapsedThreadsExtended bool
} }
type GetPostsOptions struct { type GetPostsOptions struct {
ChannelId string ChannelId string
PostId string PostId string
Page int Page int
PerPage int PerPage int
SkipFetchThreads bool SkipFetchThreads bool
CollapsedThreads bool
CollapsedThreadsExtended bool
} }
func PostFromJson(data io.Reader) *Post { func PostFromJson(data io.Reader) *Post {
@@ -357,6 +365,9 @@ func (o *Post) SanitizeProps() {
o.DelProp(member) o.DelProp(member)
} }
} }
for _, p := range o.Participants {
p.Sanitize(map[string]bool{})
}
} }
func (o *Post) PreSave() { func (o *Post) PreSave() {

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

@@ -110,8 +110,8 @@ func getMockStore() *mocks.Store {
mockPostStoreEtagResult := fmt.Sprintf("%v.%v", model.CurrentVersion, 1) mockPostStoreEtagResult := fmt.Sprintf("%v.%v", model.CurrentVersion, 1)
mockPostStore.On("ClearCaches") mockPostStore.On("ClearCaches")
mockPostStore.On("InvalidateLastPostTimeCache", "channelId") mockPostStore.On("InvalidateLastPostTimeCache", "channelId")
mockPostStore.On("GetEtag", "channelId", true).Return(mockPostStoreEtagResult) mockPostStore.On("GetEtag", "channelId", true, false).Return(mockPostStoreEtagResult)
mockPostStore.On("GetEtag", "channelId", false).Return(mockPostStoreEtagResult) mockPostStore.On("GetEtag", "channelId", false, false).Return(mockPostStoreEtagResult)
mockPostStore.On("GetPostsSince", mockPostStoreOptions, true).Return(model.NewPostList(), nil) mockPostStore.On("GetPostsSince", mockPostStoreOptions, true).Return(model.NewPostList(), nil)
mockPostStore.On("GetPostsSince", mockPostStoreOptions, false).Return(model.NewPostList(), nil) mockPostStore.On("GetPostsSince", mockPostStoreOptions, false).Return(model.NewPostList(), nil)
mockStore.On("Post").Return(&mockPostStore) mockStore.On("Post").Return(&mockPostStore)

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

@@ -59,7 +59,7 @@ func (s LocalCachePostStore) InvalidateLastPostTimeCache(channelId string) {
} }
} }
func (s LocalCachePostStore) GetEtag(channelId string, allowFromCache bool) string { func (s LocalCachePostStore) GetEtag(channelId string, allowFromCache, collapsedThreads bool) string {
if allowFromCache { if allowFromCache {
var lastTime int64 var lastTime int64
if err := s.rootStore.doStandardReadCache(s.rootStore.lastPostTimeCache, channelId, &lastTime); err == nil { if err := s.rootStore.doStandardReadCache(s.rootStore.lastPostTimeCache, channelId, &lastTime); err == nil {
@@ -67,7 +67,7 @@ func (s LocalCachePostStore) GetEtag(channelId string, allowFromCache bool) stri
} }
} }
result := s.PostStore.GetEtag(channelId, allowFromCache) result := s.PostStore.GetEtag(channelId, allowFromCache, collapsedThreads)
splittedResult := strings.Split(result, ".") splittedResult := strings.Split(result, ".")

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

@@ -36,11 +36,11 @@ func TestPostStoreLastPostTimeCache(t *testing.T) {
expectedResult := fmt.Sprintf("%v.%v", model.CurrentVersion, fakeLastTime) expectedResult := fmt.Sprintf("%v.%v", model.CurrentVersion, fakeLastTime)
etag := cachedStore.Post().GetEtag(channelId, true) etag := cachedStore.Post().GetEtag(channelId, true, false)
assert.Equal(t, etag, expectedResult) assert.Equal(t, etag, expectedResult)
mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 1) mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 1)
etag = cachedStore.Post().GetEtag(channelId, true) etag = cachedStore.Post().GetEtag(channelId, true, false)
assert.Equal(t, etag, expectedResult) assert.Equal(t, etag, expectedResult)
mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 1) mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 1)
}) })
@@ -51,9 +51,9 @@ func TestPostStoreLastPostTimeCache(t *testing.T) {
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err) require.NoError(t, err)
cachedStore.Post().GetEtag(channelId, true) cachedStore.Post().GetEtag(channelId, true, false)
mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 1) mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 1)
cachedStore.Post().GetEtag(channelId, false) cachedStore.Post().GetEtag(channelId, false, false)
mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 2) mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 2)
}) })
@@ -63,10 +63,10 @@ func TestPostStoreLastPostTimeCache(t *testing.T) {
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err) require.NoError(t, err)
cachedStore.Post().GetEtag(channelId, true) cachedStore.Post().GetEtag(channelId, true, false)
mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 1) mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 1)
cachedStore.Post().InvalidateLastPostTimeCache(channelId) cachedStore.Post().InvalidateLastPostTimeCache(channelId)
cachedStore.Post().GetEtag(channelId, true) cachedStore.Post().GetEtag(channelId, true, false)
mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 2) mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 2)
}) })
@@ -76,10 +76,10 @@ func TestPostStoreLastPostTimeCache(t *testing.T) {
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err) require.NoError(t, err)
cachedStore.Post().GetEtag(channelId, true) cachedStore.Post().GetEtag(channelId, true, false)
mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 1) mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 1)
cachedStore.Post().ClearCaches() cachedStore.Post().ClearCaches()
cachedStore.Post().GetEtag(channelId, true) cachedStore.Post().GetEtag(channelId, true, false)
mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 2) mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 2)
}) })

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

@@ -4898,7 +4898,7 @@ func (s *OpenTracingLayerPostStore) Delete(postId string, time int64, deleteByID
return err return err
} }
func (s *OpenTracingLayerPostStore) Get(id string, skipFetchThreads bool) (*model.PostList, error) { func (s *OpenTracingLayerPostStore) Get(id string, skipFetchThreads bool, collapsedThreads bool, collapsedThreadsExtended bool) (*model.PostList, error) {
origCtx := s.Root.Store.Context() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PostStore.Get") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PostStore.Get")
s.Root.Store.SetContext(newCtx) s.Root.Store.SetContext(newCtx)
@@ -4907,7 +4907,7 @@ func (s *OpenTracingLayerPostStore) Get(id string, skipFetchThreads bool) (*mode
}() }()
defer span.Finish() defer span.Finish()
result, err := s.PostStore.Get(id, skipFetchThreads) result, err := s.PostStore.Get(id, skipFetchThreads, collapsedThreads, collapsedThreadsExtended)
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)
@@ -4934,7 +4934,7 @@ func (s *OpenTracingLayerPostStore) GetDirectPostParentsForExportAfter(limit int
return result, err return result, err
} }
func (s *OpenTracingLayerPostStore) GetEtag(channelId string, allowFromCache bool) string { func (s *OpenTracingLayerPostStore) GetEtag(channelId string, allowFromCache bool, collapsedThreads bool) string {
origCtx := s.Root.Store.Context() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PostStore.GetEtag") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PostStore.GetEtag")
s.Root.Store.SetContext(newCtx) s.Root.Store.SetContext(newCtx)
@@ -4943,7 +4943,7 @@ func (s *OpenTracingLayerPostStore) GetEtag(channelId string, allowFromCache boo
}() }()
defer span.Finish() defer span.Finish()
result := s.PostStore.GetEtag(channelId, allowFromCache) result := s.PostStore.GetEtag(channelId, allowFromCache, collapsedThreads)
return result return result
} }

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

@@ -5302,11 +5302,11 @@ func (s *RetryLayerPostStore) Delete(postId string, time int64, deleteByID strin
} }
func (s *RetryLayerPostStore) Get(id string, skipFetchThreads bool) (*model.PostList, error) { func (s *RetryLayerPostStore) Get(id string, skipFetchThreads bool, collapsedThreads bool, collapsedThreadsExtended bool) (*model.PostList, error) {
tries := 0 tries := 0
for { for {
result, err := s.PostStore.Get(id, skipFetchThreads) result, err := s.PostStore.Get(id, skipFetchThreads, collapsedThreads, collapsedThreadsExtended)
if err == nil { if err == nil {
return result, nil return result, nil
} }
@@ -5342,9 +5342,9 @@ func (s *RetryLayerPostStore) GetDirectPostParentsForExportAfter(limit int, afte
} }
func (s *RetryLayerPostStore) GetEtag(channelId string, allowFromCache bool) string { func (s *RetryLayerPostStore) GetEtag(channelId string, allowFromCache bool, collapsedThreads bool) string {
return s.PostStore.GetEtag(channelId, allowFromCache) return s.PostStore.GetEtag(channelId, allowFromCache, collapsedThreads)
} }

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

@@ -108,7 +108,7 @@ func (s SearchPostStore) Delete(postId string, date int64, deletedByID string) e
err := s.PostStore.Delete(postId, date, deletedByID) err := s.PostStore.Delete(postId, date, deletedByID)
if err == nil { if err == nil {
postList, err2 := s.PostStore.Get(postId, true) postList, err2 := s.PostStore.Get(postId, true, false, false)
if postList != nil && len(postList.Order) > 0 { if postList != nil && len(postList.Order) > 0 {
if err2 != nil { if err2 != nil {
s.deletePostIndex(postList.Posts[postList.Order[0]]) s.deletePostIndex(postList.Posts[postList.Order[0]])

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

@@ -30,6 +30,12 @@ type SqlPostStore struct {
maxPostSizeCached int maxPostSizeCached int
} }
type postWithExtra struct {
ThreadReplyCount int64
ThreadParticipants model.StringArray
model.Post
}
func (s *SqlPostStore) ClearCaches() { func (s *SqlPostStore) ClearCaches() {
} }
@@ -418,8 +424,40 @@ func (s *SqlPostStore) GetFlaggedPostsForChannel(userId, channelId string, offse
return pl, nil return pl, nil
} }
func (s *SqlPostStore) getPostWithCollapsedThreads(id string, extended bool) (*model.PostList, error) {
if len(id) == 0 {
return nil, store.NewErrInvalidInput("Post", "id", id)
}
func (s *SqlPostStore) Get(id string, skipFetchThreads bool) (*model.PostList, error) { var columns []string
for _, c := range postSliceColumns() {
columns = append(columns, "Posts."+c)
}
columns = append(columns, "COALESCE(Threads.ReplyCount, 0) as ThreadReplyCount", "COALESCE(Threads.LastReplyAt, 0) as LastReplyAt", "COALESCE(Threads.Participants, '[]') as ThreadParticipants")
var post postWithExtra
postFetchQuery, args, _ := s.getQueryBuilder().
Select(columns...).
From("Posts").
LeftJoin("Threads ON Threads.PostId = Id").
Where(sq.Eq{"DeleteAt": 0}).
Where(sq.Eq{"Id": id}).ToSql()
err := s.GetReplica().SelectOne(&post, postFetchQuery, args...)
if err != nil {
if err == sql.ErrNoRows {
return nil, store.NewErrNotFound("Post", id)
}
return nil, errors.Wrapf(err, "failed to get Post with id=%s", id)
}
return s.prepareThreadedResponse([]*postWithExtra{&post}, extended, false)
}
func (s *SqlPostStore) Get(id string, skipFetchThreads, collapsedThreads, collapsedThreadsExtended bool) (*model.PostList, error) {
if collapsedThreads {
return s.getPostWithCollapsedThreads(id, collapsedThreadsExtended)
}
pl := model.NewPostList() pl := model.NewPostList()
if len(id) == 0 { if len(id) == 0 {
@@ -484,9 +522,14 @@ type etagPosts struct {
func (s *SqlPostStore) InvalidateLastPostTimeCache(channelId string) { func (s *SqlPostStore) InvalidateLastPostTimeCache(channelId string) {
} }
func (s *SqlPostStore) GetEtag(channelId string, allowFromCache bool) string { func (s *SqlPostStore) GetEtag(channelId string, allowFromCache, collapsedThreads bool) string {
q := s.getQueryBuilder().Select("Id", "UpdateAt").From("Posts").Where(sq.Eq{"ChannelId": channelId}).OrderBy("UpdateAt DESC").Limit(1)
if collapsedThreads {
q.Where(sq.Eq{"RootId": ""})
}
sql, args, _ := q.ToSql()
var et etagPosts var et etagPosts
err := s.GetReplica().SelectOne(&et, "SELECT Id, UpdateAt FROM Posts WHERE ChannelId = :ChannelId ORDER BY UpdateAt DESC LIMIT 1", map[string]interface{}{"ChannelId": channelId}) err := s.GetReplica().SelectOne(&et, sql, args...)
var result string var result string
if err != nil { if err != nil {
result = fmt.Sprintf("%v.%v", model.CurrentVersion, model.GetMillis()) result = fmt.Sprintf("%v.%v", model.CurrentVersion, model.GetMillis())
@@ -616,10 +659,102 @@ func (s *SqlPostStore) PermanentDeleteByChannel(channelId string) error {
return nil return nil
} }
func (s *SqlPostStore) prepareThreadedResponse(posts []*postWithExtra, extended, reversed bool) (*model.PostList, error) {
list := model.NewPostList()
var userIds []string
userIdMap := map[string]bool{}
for _, thread := range posts {
for _, participantId := range thread.ThreadParticipants {
if _, ok := userIdMap[participantId]; !ok {
userIdMap[participantId] = true
userIds = append(userIds, participantId)
}
}
}
var users []*model.User
if extended {
var err error
users, err = s.User().GetProfileByIds(userIds, &store.UserGetByIdsOpts{}, true)
if err != nil {
return nil, err
}
} else {
for _, userId := range userIds {
users = append(users, &model.User{Id: userId})
}
}
processPost := func(p *postWithExtra) error {
p.Post.ReplyCount = p.ThreadReplyCount
for _, th := range p.ThreadParticipants {
var participant *model.User
for _, u := range users {
if u.Id == th {
participant = u
break
}
}
if participant == nil {
return errors.New("cannot find thread participant with id=" + th)
}
p.Post.Participants = append(p.Post.Participants, participant)
}
return nil
}
l := len(posts)
for i := range posts {
idx := i
// We need to flip the order if we selected backwards
if reversed {
idx = l - i - 1
}
if err := processPost(posts[idx]); err != nil {
return nil, err
}
list.AddPost(&posts[idx].Post)
list.AddOrder(posts[idx].Id)
}
return list, nil
}
func (s *SqlPostStore) getPostsCollapsedThreads(options model.GetPostsOptions) (*model.PostList, error) {
var columns []string
for _, c := range postSliceColumns() {
columns = append(columns, "Posts."+c)
}
columns = append(columns, "COALESCE(Threads.ReplyCount, 0) as ThreadReplyCount", "COALESCE(Threads.LastReplyAt, 0) as LastReplyAt", "COALESCE(Threads.Participants, '[]') as ThreadParticipants")
var posts []*postWithExtra
offset := options.PerPage * options.Page
postFetchQuery, args, _ := s.getQueryBuilder().
Select(columns...).
From("Posts").
LeftJoin("Threads ON Threads.PostId = Id").
Where(sq.Eq{"DeleteAt": 0}).
Where(sq.Eq{"Posts.ChannelId": options.ChannelId}).
Where(sq.Eq{"RootId": ""}).
Limit(uint64(options.PerPage)).
Offset(uint64(offset)).
OrderBy("CreateAt DESC").ToSql()
_, err := s.GetReplica().Select(&posts, postFetchQuery, args...)
if err != nil {
return nil, errors.Wrapf(err, "failed to find Posts with channelId=%s", options.ChannelId)
}
return s.prepareThreadedResponse(posts, options.CollapsedThreadsExtended, false)
}
func (s *SqlPostStore) GetPosts(options model.GetPostsOptions, _ bool) (*model.PostList, error) { func (s *SqlPostStore) GetPosts(options model.GetPostsOptions, _ bool) (*model.PostList, error) {
if options.PerPage > 1000 { if options.PerPage > 1000 {
return nil, store.NewErrInvalidInput("Post", "<options.PerPage>", options.PerPage) return nil, store.NewErrInvalidInput("Post", "<options.PerPage>", options.PerPage)
} }
if options.CollapsedThreads {
return s.getPostsCollapsedThreads(options)
}
offset := options.PerPage * options.Page offset := options.PerPage * options.Page
rpc := make(chan store.StoreResult, 1) rpc := make(chan store.StoreResult, 1)
@@ -664,7 +799,36 @@ func (s *SqlPostStore) GetPosts(options model.GetPostsOptions, _ bool) (*model.P
return list, nil return list, nil
} }
func (s *SqlPostStore) getPostsSinceCollapsedThreads(options model.GetPostsSinceOptions) (*model.PostList, error) {
var columns []string
for _, c := range postSliceColumns() {
columns = append(columns, "Posts."+c)
}
columns = append(columns, "COALESCE(Threads.ReplyCount, 0) as ThreadReplyCount", "COALESCE(Threads.LastReplyAt, 0) as LastReplyAt", "COALESCE(Threads.Participants, '[]') as ThreadParticipants")
var posts []*postWithExtra
postFetchQuery, args, _ := s.getQueryBuilder().
Select(columns...).
From("Posts").
LeftJoin("Threads ON Threads.PostId = Id").
Where(sq.Eq{"DeleteAt": 0}).
Where(sq.Eq{"Posts.ChannelId": options.ChannelId}).
Where(sq.Gt{"UpdateAt": options.Time}).
Where(sq.Eq{"RootId": ""}).
OrderBy("CreateAt DESC").ToSql()
_, err := s.GetReplica().Select(&posts, postFetchQuery, args...)
if err != nil {
return nil, errors.Wrapf(err, "failed to find Posts with channelId=%s", options.ChannelId)
}
return s.prepareThreadedResponse(posts, options.CollapsedThreadsExtended, false)
}
func (s *SqlPostStore) GetPostsSince(options model.GetPostsSinceOptions, allowFromCache bool) (*model.PostList, error) { func (s *SqlPostStore) GetPostsSince(options model.GetPostsSinceOptions, allowFromCache bool) (*model.PostList, error) {
if options.CollapsedThreads {
return s.getPostsSinceCollapsedThreads(options)
}
var posts []*model.Post var posts []*model.Post
replyCountQuery1 := "" replyCountQuery1 := ""
@@ -753,7 +917,8 @@ func (s *SqlPostStore) getPostsAround(before bool, options model.GetPostsOptions
} }
offset := options.Page * options.PerPage offset := options.Page * options.PerPage
var posts, parents []*model.Post var posts []*postWithExtra
var parents []*model.Post
var direction string var direction string
var sort string var sort string
@@ -771,20 +936,30 @@ func (s *SqlPostStore) getPostsAround(before bool, options model.GetPostsOptions
if s.DriverName() == model.DATABASE_DRIVER_MYSQL { if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
table += " USE INDEX(idx_posts_channel_id_delete_at_create_at)" table += " USE INDEX(idx_posts_channel_id_delete_at_create_at)"
} }
columns := []string{"p.*"}
if options.CollapsedThreads {
columns = append(columns, "COALESCE(Threads.ReplyCount, 0) as ThreadReplyCount", "COALESCE(Threads.LastReplyAt, 0) as LastReplyAt", "COALESCE(Threads.Participants, '[]') as ThreadParticipants")
}
query := s.getQueryBuilder().Select(columns...)
replyCountSubQuery := s.getQueryBuilder().Select("COUNT(Posts.Id)").From("Posts").Where(sq.Expr("Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0")) replyCountSubQuery := s.getQueryBuilder().Select("COUNT(Posts.Id)").From("Posts").Where(sq.Expr("Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0"))
query := s.getQueryBuilder().Select("p.*")
query = query.Column(sq.Alias(replyCountSubQuery, "ReplyCount")) conditions := sq.And{
sq.Expr(`CreateAt `+direction+` (SELECT CreateAt FROM Posts WHERE Id = ?)`, options.PostId),
sq.Eq{"p.ChannelId": options.ChannelId},
sq.Eq{"DeleteAt": int(0)},
}
if options.CollapsedThreads {
conditions = append(conditions, sq.Eq{"RootId": ""})
query = query.LeftJoin("Threads ON Threads.PostId = p.Id")
} else {
query = query.Column(sq.Alias(replyCountSubQuery, "ReplyCount"))
}
query = query.From(table). query = query.From(table).
Where(sq.And{ Where(conditions).
sq.Expr(`CreateAt `+direction+` (SELECT CreateAt FROM Posts WHERE Id = ?)`, options.PostId),
sq.Eq{"ChannelId": options.ChannelId},
sq.Eq{"DeleteAt": int(0)},
}).
// Adding ChannelId and DeleteAt order columns // Adding ChannelId and DeleteAt order columns
// to let mysql choose the "idx_posts_channel_id_delete_at_create_at" index always. // to let mysql choose the "idx_posts_channel_id_delete_at_create_at" index always.
// See MM-24170. // See MM-24170.
OrderBy("ChannelId", "DeleteAt", "CreateAt "+sort). OrderBy("p.ChannelId", "DeleteAt", "CreateAt "+sort).
Limit(uint64(options.PerPage)). Limit(uint64(options.PerPage)).
Offset(uint64(offset)) Offset(uint64(offset))
@@ -797,7 +972,7 @@ func (s *SqlPostStore) getPostsAround(before bool, options model.GetPostsOptions
return nil, errors.Wrapf(err, "failed to find Posts with channelId=%s", options.ChannelId) return nil, errors.Wrapf(err, "failed to find Posts with channelId=%s", options.ChannelId)
} }
if len(posts) > 0 { if !options.CollapsedThreads && len(posts) > 0 {
rootIds := []string{} rootIds := []string{}
for _, post := range posts { for _, post := range posts {
rootIds = append(rootIds, post.Id) rootIds = append(rootIds, post.Id)
@@ -822,31 +997,20 @@ func (s *SqlPostStore) getPostsAround(before bool, options model.GetPostsOptions
}). }).
OrderBy("CreateAt DESC") OrderBy("CreateAt DESC")
rootQueryString, rootArgs, err := rootQuery.ToSql() rootQueryString, rootArgs, nErr := rootQuery.ToSql()
if err != nil { if nErr != nil {
return nil, errors.Wrap(err, "post_tosql") return nil, errors.Wrap(nErr, "post_tosql")
} }
_, err = s.GetMaster().Select(&parents, rootQueryString, rootArgs...) _, nErr = s.GetMaster().Select(&parents, rootQueryString, rootArgs...)
if err != nil { if nErr != nil {
return nil, errors.Wrapf(err, "failed to find Posts with channelId=%s", options.ChannelId) return nil, errors.Wrapf(nErr, "failed to find Posts with channelId=%s", options.ChannelId)
} }
} }
list := model.NewPostList() list, err := s.prepareThreadedResponse(posts, options.CollapsedThreadsExtended, !before)
if err != nil {
// We need to flip the order if we selected backwards return nil, err
if before {
for _, p := range posts {
list.AddPost(p)
list.AddOrder(p.Id)
}
} else {
l := len(posts)
for i := range posts {
list.AddPost(posts[l-i-1])
list.AddOrder(posts[l-i-1].Id)
}
} }
for _, p := range parents { for _, p := range parents {

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

@@ -234,8 +234,9 @@ func (s *SqlThreadStore) GetThreadsForUser(userId, teamId string, opts model.Get
} }
var users []*model.User var users []*model.User
if opts.Extended { if opts.Extended {
query, args, _ := s.getQueryBuilder().Select("*").From("Users").Where(sq.Eq{"Id": userIds}).ToSql() var err error
if _, err := s.GetReplica().Select(&users, query, args...); err != nil { users, err = s.User().GetProfileByIds(userIds, &store.UserGetByIdsOpts{}, true)
if err != nil {
return nil, errors.Wrapf(err, "failed to get threads for user id=%s", userId) return nil, errors.Wrapf(err, "failed to get threads for user id=%s", userId)
} }
} else { } else {
@@ -414,6 +415,18 @@ func (s *SqlThreadStore) CreateMembershipIfNeeded(userId, postId string, followi
LastUpdated: now, LastUpdated: now,
UnreadMentions: int64(mentions), UnreadMentions: int64(mentions),
}) })
if err != nil {
return err
}
thread, err := s.Get(postId)
if err != nil {
return err
}
if !thread.Participants.Contains(userId) {
thread.Participants = append(thread.Participants, userId)
_, err = s.Update(thread)
}
return err return err
} }

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

@@ -964,6 +964,7 @@ func upgradeDatabaseToVersion532(sqlStore *SqlStore) {
// if shouldPerformUpgrade(sqlStore, Version5310, Version5320) { // if shouldPerformUpgrade(sqlStore, Version5310, Version5320) {
// allow 10 files per post // allow 10 files per post
sqlStore.AlterColumnTypeIfExists("Posts", "FileIds", "text", "varchar(300)") sqlStore.AlterColumnTypeIfExists("Posts", "FileIds", "text", "varchar(300)")
sqlStore.CreateColumnIfNotExists("ThreadMemberships", "UnreadMentions", "bigint", "bigint", "0")
sqlStore.CreateColumnIfNotExistsNoDefault("Channels", "Shared", "tinyint(1)", "boolean") sqlStore.CreateColumnIfNotExistsNoDefault("Channels", "Shared", "tinyint(1)", "boolean")
sqlStore.CreateColumnIfNotExists("ThreadMemberships", "UnreadMentions", "bigint", "bigint", "0") sqlStore.CreateColumnIfNotExists("ThreadMemberships", "UnreadMentions", "bigint", "bigint", "0")
// saveSchemaVersion(sqlStore, Version5320) // saveSchemaVersion(sqlStore, Version5320)

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

@@ -272,7 +272,7 @@ type PostStore interface {
SaveMultiple(posts []*model.Post) ([]*model.Post, int, error) SaveMultiple(posts []*model.Post) ([]*model.Post, int, error)
Save(post *model.Post) (*model.Post, error) Save(post *model.Post) (*model.Post, error)
Update(newPost *model.Post, oldPost *model.Post) (*model.Post, error) Update(newPost *model.Post, oldPost *model.Post) (*model.Post, error)
Get(id string, skipFetchThreads bool) (*model.PostList, error) Get(id string, skipFetchThreads, collapsedThreads, collapsedThreadsExtended bool) (*model.PostList, error)
GetSingle(id string) (*model.Post, error) GetSingle(id string) (*model.Post, error)
Delete(postId string, time int64, deleteByID string) error Delete(postId string, time int64, deleteByID string) error
PermanentDeleteByUser(userId string) error PermanentDeleteByUser(userId string) error
@@ -288,7 +288,7 @@ type PostStore interface {
GetPostAfterTime(channelId string, time int64) (*model.Post, error) GetPostAfterTime(channelId string, time int64) (*model.Post, error)
GetPostIdAfterTime(channelId string, time int64) (string, error) GetPostIdAfterTime(channelId string, time int64) (string, error)
GetPostIdBeforeTime(channelId string, time int64) (string, error) GetPostIdBeforeTime(channelId string, time int64) (string, error)
GetEtag(channelId string, allowFromCache bool) string GetEtag(channelId string, allowFromCache bool, collapsedThreads bool) string
Search(teamId string, userId string, params *model.SearchParams) (*model.PostList, error) Search(teamId string, userId string, params *model.SearchParams) (*model.PostList, error)
AnalyticsUserCountsWithPostsByDay(teamId string) (model.AnalyticsRows, error) AnalyticsUserCountsWithPostsByDay(teamId string) (model.AnalyticsRows, error)
AnalyticsPostCountsByDay(options *model.AnalyticsPostCountsOptions) (model.AnalyticsRows, error) AnalyticsPostCountsByDay(options *model.AnalyticsPostCountsOptions) (model.AnalyticsRows, error)

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

@@ -100,13 +100,13 @@ func (_m *PostStore) Delete(postId string, time int64, deleteByID string) error
return r0 return r0
} }
// Get provides a mock function with given fields: id, skipFetchThreads // Get provides a mock function with given fields: id, skipFetchThreads, collapsedThreads, collapsedThreadsExtended
func (_m *PostStore) Get(id string, skipFetchThreads bool) (*model.PostList, error) { func (_m *PostStore) Get(id string, skipFetchThreads bool, collapsedThreads bool, collapsedThreadsExtended bool) (*model.PostList, error) {
ret := _m.Called(id, skipFetchThreads) ret := _m.Called(id, skipFetchThreads, collapsedThreads, collapsedThreadsExtended)
var r0 *model.PostList var r0 *model.PostList
if rf, ok := ret.Get(0).(func(string, bool) *model.PostList); ok { if rf, ok := ret.Get(0).(func(string, bool, bool, bool) *model.PostList); ok {
r0 = rf(id, skipFetchThreads) r0 = rf(id, skipFetchThreads, collapsedThreads, collapsedThreadsExtended)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.PostList) r0 = ret.Get(0).(*model.PostList)
@@ -114,8 +114,8 @@ func (_m *PostStore) Get(id string, skipFetchThreads bool) (*model.PostList, err
} }
var r1 error var r1 error
if rf, ok := ret.Get(1).(func(string, bool) error); ok { if rf, ok := ret.Get(1).(func(string, bool, bool, bool) error); ok {
r1 = rf(id, skipFetchThreads) r1 = rf(id, skipFetchThreads, collapsedThreads, collapsedThreadsExtended)
} else { } else {
r1 = ret.Error(1) r1 = ret.Error(1)
} }
@@ -146,13 +146,13 @@ func (_m *PostStore) GetDirectPostParentsForExportAfter(limit int, afterId strin
return r0, r1 return r0, r1
} }
// GetEtag provides a mock function with given fields: channelId, allowFromCache // GetEtag provides a mock function with given fields: channelId, allowFromCache, collapsedThreads
func (_m *PostStore) GetEtag(channelId string, allowFromCache bool) string { func (_m *PostStore) GetEtag(channelId string, allowFromCache bool, collapsedThreads bool) string {
ret := _m.Called(channelId, allowFromCache) ret := _m.Called(channelId, allowFromCache, collapsedThreads)
var r0 string var r0 string
if rf, ok := ret.Get(0).(func(string, bool) string); ok { if rf, ok := ret.Get(0).(func(string, bool, bool) string); ok {
r0 = rf(channelId, allowFromCache) r0 = rf(channelId, allowFromCache, collapsedThreads)
} else { } else {
r0 = ret.Get(0).(string) r0 = ret.Get(0).(string)
} }

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

@@ -407,23 +407,23 @@ func testPostStoreGet(t *testing.T, ss store.Store) {
o1.UserId = model.NewId() o1.UserId = model.NewId()
o1.Message = "zz" + model.NewId() + "b" o1.Message = "zz" + model.NewId() + "b"
etag1 := ss.Post().GetEtag(o1.ChannelId, false) etag1 := ss.Post().GetEtag(o1.ChannelId, false, false)
require.Equal(t, 0, strings.Index(etag1, model.CurrentVersion+"."), "Invalid Etag") require.Equal(t, 0, strings.Index(etag1, model.CurrentVersion+"."), "Invalid Etag")
o1, err := ss.Post().Save(o1) o1, err := ss.Post().Save(o1)
require.Nil(t, err) require.Nil(t, err)
etag2 := ss.Post().GetEtag(o1.ChannelId, false) etag2 := ss.Post().GetEtag(o1.ChannelId, false, false)
require.Equal(t, 0, strings.Index(etag2, fmt.Sprintf("%v.%v", model.CurrentVersion, o1.UpdateAt)), "Invalid Etag") require.Equal(t, 0, strings.Index(etag2, fmt.Sprintf("%v.%v", model.CurrentVersion, o1.UpdateAt)), "Invalid Etag")
r1, err := ss.Post().Get(o1.Id, false) r1, err := ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, r1.Posts[o1.Id].CreateAt, o1.CreateAt, "invalid returned post") require.Equal(t, r1.Posts[o1.Id].CreateAt, o1.CreateAt, "invalid returned post")
_, err = ss.Post().Get("123", false) _, err = ss.Post().Get("123", false, false, false)
require.NotNil(t, err, "Missing id should have failed") require.NotNil(t, err, "Missing id should have failed")
_, err = ss.Post().Get("", false) _, err = ss.Post().Get("", false, false, false)
require.NotNil(t, err, "should fail for blank post ids") require.NotNil(t, err, "should fail for blank post ids")
} }
@@ -468,15 +468,15 @@ func testPostStoreUpdate(t *testing.T, ss store.Store) {
o3, err = ss.Post().Save(o3) o3, err = ss.Post().Save(o3)
require.Nil(t, err) require.Nil(t, err)
r1, err := ss.Post().Get(o1.Id, false) r1, err := ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro1 := r1.Posts[o1.Id] ro1 := r1.Posts[o1.Id]
r2, err := ss.Post().Get(o1.Id, false) r2, err := ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro2 := r2.Posts[o2.Id] ro2 := r2.Posts[o2.Id]
r3, err := ss.Post().Get(o3.Id, false) r3, err := ss.Post().Get(o3.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro3 := r3.Posts[o3.Id] ro3 := r3.Posts[o3.Id]
@@ -487,7 +487,7 @@ func testPostStoreUpdate(t *testing.T, ss store.Store) {
_, err = ss.Post().Update(o1a, ro1) _, err = ss.Post().Update(o1a, ro1)
require.Nil(t, err) require.Nil(t, err)
r1, err = ss.Post().Get(o1.Id, false) r1, err = ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro1a := r1.Posts[o1.Id] ro1a := r1.Posts[o1.Id]
@@ -498,7 +498,7 @@ func testPostStoreUpdate(t *testing.T, ss store.Store) {
_, err = ss.Post().Update(o2a, ro2) _, err = ss.Post().Update(o2a, ro2)
require.Nil(t, err) require.Nil(t, err)
r2, err = ss.Post().Get(o1.Id, false) r2, err = ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro2a := r2.Posts[o2.Id] ro2a := r2.Posts[o2.Id]
@@ -509,7 +509,7 @@ func testPostStoreUpdate(t *testing.T, ss store.Store) {
_, err = ss.Post().Update(o3a, ro3) _, err = ss.Post().Update(o3a, ro3)
require.Nil(t, err) require.Nil(t, err)
r3, err = ss.Post().Get(o3.Id, false) r3, err = ss.Post().Get(o3.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro3a := r3.Posts[o3.Id] ro3a := r3.Posts[o3.Id]
@@ -525,7 +525,7 @@ func testPostStoreUpdate(t *testing.T, ss store.Store) {
}) })
require.Nil(t, err) require.Nil(t, err)
r4, err := ss.Post().Get(o4.Id, false) r4, err := ss.Post().Get(o4.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro4 := r4.Posts[o4.Id] ro4 := r4.Posts[o4.Id]
@@ -535,7 +535,7 @@ func testPostStoreUpdate(t *testing.T, ss store.Store) {
_, err = ss.Post().Update(o4a, ro4) _, err = ss.Post().Update(o4a, ro4)
require.Nil(t, err) require.Nil(t, err)
r4, err = ss.Post().Get(o4.Id, false) r4, err = ss.Post().Get(o4.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro4a := r4.Posts[o4.Id] ro4a := r4.Posts[o4.Id]
@@ -550,13 +550,13 @@ func testPostStoreDelete(t *testing.T, ss store.Store) {
o1.Message = "zz" + model.NewId() + "b" o1.Message = "zz" + model.NewId() + "b"
deleteByID := model.NewId() deleteByID := model.NewId()
etag1 := ss.Post().GetEtag(o1.ChannelId, false) etag1 := ss.Post().GetEtag(o1.ChannelId, false, false)
require.Equal(t, 0, strings.Index(etag1, model.CurrentVersion+"."), "Invalid Etag") require.Equal(t, 0, strings.Index(etag1, model.CurrentVersion+"."), "Invalid Etag")
o1, err := ss.Post().Save(o1) o1, err := ss.Post().Save(o1)
require.Nil(t, err) require.Nil(t, err)
r1, err := ss.Post().Get(o1.Id, false) r1, err := ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, r1.Posts[o1.Id].CreateAt, o1.CreateAt, "invalid returned post") require.Equal(t, r1.Posts[o1.Id].CreateAt, o1.CreateAt, "invalid returned post")
@@ -569,10 +569,10 @@ func testPostStoreDelete(t *testing.T, ss store.Store) {
assert.Equal(t, deleteByID, actual, "Expected (*Post).Props[model.POST_PROPS_DELETE_BY] to be %v but got %v.", deleteByID, actual) assert.Equal(t, deleteByID, actual, "Expected (*Post).Props[model.POST_PROPS_DELETE_BY] to be %v but got %v.", deleteByID, actual)
r3, err := ss.Post().Get(o1.Id, false) r3, err := ss.Post().Get(o1.Id, false, false, false)
require.NotNil(t, err, "Missing id should have failed - PostList %v", r3) require.NotNil(t, err, "Missing id should have failed - PostList %v", r3)
etag2 := ss.Post().GetEtag(o1.ChannelId, false) etag2 := ss.Post().GetEtag(o1.ChannelId, false, false)
require.Equal(t, 0, strings.Index(etag2, model.CurrentVersion+"."), "Invalid Etag") require.Equal(t, 0, strings.Index(etag2, model.CurrentVersion+"."), "Invalid Etag")
} }
@@ -596,10 +596,10 @@ func testPostStoreDelete1Level(t *testing.T, ss store.Store) {
err = ss.Post().Delete(o1.Id, model.GetMillis(), "") err = ss.Post().Delete(o1.Id, model.GetMillis(), "")
require.Nil(t, err) require.Nil(t, err)
_, err = ss.Post().Get(o1.Id, false) _, err = ss.Post().Get(o1.Id, false, false, false)
require.NotNil(t, err, "Deleted id should have failed") require.NotNil(t, err, "Deleted id should have failed")
_, err = ss.Post().Get(o2.Id, false) _, err = ss.Post().Get(o2.Id, false, false, false)
require.NotNil(t, err, "Deleted id should have failed") require.NotNil(t, err, "Deleted id should have failed")
} }
@@ -639,16 +639,16 @@ func testPostStoreDelete2Level(t *testing.T, ss store.Store) {
err = ss.Post().Delete(o1.Id, model.GetMillis(), "") err = ss.Post().Delete(o1.Id, model.GetMillis(), "")
require.Nil(t, err) require.Nil(t, err)
_, err = ss.Post().Get(o1.Id, false) _, err = ss.Post().Get(o1.Id, false, false, false)
require.NotNil(t, err, "Deleted id should have failed") require.NotNil(t, err, "Deleted id should have failed")
_, err = ss.Post().Get(o2.Id, false) _, err = ss.Post().Get(o2.Id, false, false, false)
require.NotNil(t, err, "Deleted id should have failed") require.NotNil(t, err, "Deleted id should have failed")
_, err = ss.Post().Get(o3.Id, false) _, err = ss.Post().Get(o3.Id, false, false, false)
require.NotNil(t, err, "Deleted id should have failed") require.NotNil(t, err, "Deleted id should have failed")
_, err = ss.Post().Get(o4.Id, false) _, err = ss.Post().Get(o4.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
} }
@@ -679,16 +679,16 @@ func testPostStorePermDelete1Level(t *testing.T, ss store.Store) {
err2 := ss.Post().PermanentDeleteByUser(o2.UserId) err2 := ss.Post().PermanentDeleteByUser(o2.UserId)
require.Nil(t, err2) require.Nil(t, err2)
_, err = ss.Post().Get(o1.Id, false) _, err = ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, err, "Deleted id shouldn't have failed") require.Nil(t, err, "Deleted id shouldn't have failed")
_, err = ss.Post().Get(o2.Id, false) _, err = ss.Post().Get(o2.Id, false, false, false)
require.NotNil(t, err, "Deleted id should have failed") require.NotNil(t, err, "Deleted id should have failed")
err = ss.Post().PermanentDeleteByChannel(o3.ChannelId) err = ss.Post().PermanentDeleteByChannel(o3.ChannelId)
require.Nil(t, err) require.Nil(t, err)
_, err = ss.Post().Get(o3.Id, false) _, err = ss.Post().Get(o3.Id, false, false, false)
require.NotNil(t, err, "Deleted id should have failed") require.NotNil(t, err, "Deleted id should have failed")
} }
@@ -719,13 +719,13 @@ func testPostStorePermDelete1Level2(t *testing.T, ss store.Store) {
err2 := ss.Post().PermanentDeleteByUser(o1.UserId) err2 := ss.Post().PermanentDeleteByUser(o1.UserId)
require.Nil(t, err2) require.Nil(t, err2)
_, err = ss.Post().Get(o1.Id, false) _, err = ss.Post().Get(o1.Id, false, false, false)
require.NotNil(t, err, "Deleted id should have failed") require.NotNil(t, err, "Deleted id should have failed")
_, err = ss.Post().Get(o2.Id, false) _, err = ss.Post().Get(o2.Id, false, false, false)
require.NotNil(t, err, "Deleted id should have failed") require.NotNil(t, err, "Deleted id should have failed")
_, err = ss.Post().Get(o3.Id, false) _, err = ss.Post().Get(o3.Id, false, false, false)
require.Nil(t, err, "Deleted id should have failed") require.Nil(t, err, "Deleted id should have failed")
} }
@@ -755,7 +755,7 @@ func testPostStoreGetWithChildren(t *testing.T, ss store.Store) {
o3, err = ss.Post().Save(o3) o3, err = ss.Post().Save(o3)
require.Nil(t, err) require.Nil(t, err)
pl, err := ss.Post().Get(o1.Id, false) pl, err := ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
require.Len(t, pl.Posts, 3, "invalid returned post") require.Len(t, pl.Posts, 3, "invalid returned post")
@@ -763,7 +763,7 @@ func testPostStoreGetWithChildren(t *testing.T, ss store.Store) {
dErr := ss.Post().Delete(o3.Id, model.GetMillis(), "") dErr := ss.Post().Delete(o3.Id, model.GetMillis(), "")
require.Nil(t, dErr) require.Nil(t, dErr)
pl, err = ss.Post().Get(o1.Id, false) pl, err = ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
require.Len(t, pl.Posts, 2, "invalid returned post") require.Len(t, pl.Posts, 2, "invalid returned post")
@@ -771,7 +771,7 @@ func testPostStoreGetWithChildren(t *testing.T, ss store.Store) {
dErr = ss.Post().Delete(o2.Id, model.GetMillis(), "") dErr = ss.Post().Delete(o2.Id, model.GetMillis(), "")
require.Nil(t, dErr) require.Nil(t, dErr)
pl, err = ss.Post().Get(o1.Id, false) pl, err = ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
require.Len(t, pl.Posts, 1, "invalid returned post") require.Len(t, pl.Posts, 1, "invalid returned post")
@@ -1181,6 +1181,101 @@ func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) {
}, postList.Posts) }, postList.Posts)
}) })
}) })
t.Run("with threads (collapsedThreads)", func(t *testing.T) {
channelId := model.NewId()
userId := model.NewId()
// This creates a series of posts that looks like:
// post1
// post2
// post3 (in response to post1)
// post4 (in response to post2)
// post5
// post6 (in response to post2)
post1, err := ss.Post().Save(&model.Post{
ChannelId: channelId,
UserId: userId,
Message: "post1",
})
require.Nil(t, err)
post1.ReplyCount = 1
time.Sleep(time.Millisecond)
post2, err := ss.Post().Save(&model.Post{
ChannelId: channelId,
UserId: userId,
Message: "post2",
})
require.Nil(t, err)
post2.ReplyCount = 2
time.Sleep(time.Millisecond)
post3, err := ss.Post().Save(&model.Post{
ChannelId: channelId,
UserId: userId,
ParentId: post1.Id,
RootId: post1.Id,
Message: "post3",
})
require.Nil(t, err)
post3.ReplyCount = 1
time.Sleep(time.Millisecond)
post4, err := ss.Post().Save(&model.Post{
ChannelId: channelId,
UserId: userId,
RootId: post2.Id,
ParentId: post2.Id,
Message: "post4",
})
require.Nil(t, err)
post4.ReplyCount = 2
time.Sleep(time.Millisecond)
post5, err := ss.Post().Save(&model.Post{
ChannelId: channelId,
UserId: userId,
Message: "post5",
})
require.Nil(t, err)
time.Sleep(time.Millisecond)
post6, err := ss.Post().Save(&model.Post{
ChannelId: channelId,
UserId: userId,
ParentId: post2.Id,
RootId: post2.Id,
Message: "post6",
})
post6.ReplyCount = 2
require.Nil(t, err)
// Adding a post to a thread changes the UpdateAt timestamp of the parent post
post1.UpdateAt = post3.UpdateAt
post2.UpdateAt = post6.UpdateAt
t.Run("should return each root post before a post", func(t *testing.T) {
postList, err := ss.Post().GetPostsBefore(model.GetPostsOptions{ChannelId: channelId, PostId: post4.Id, PerPage: 2, CollapsedThreads: true})
assert.Nil(t, err)
assert.Equal(t, []string{post2.Id, post1.Id}, postList.Order)
})
t.Run("should return each root post before a post with limit", func(t *testing.T) {
postList, err := ss.Post().GetPostsBefore(model.GetPostsOptions{ChannelId: channelId, PostId: post4.Id, PerPage: 1, CollapsedThreads: true})
assert.Nil(t, err)
assert.Equal(t, []string{post2.Id}, postList.Order)
})
t.Run("should return each root after a post", func(t *testing.T) {
postList, err := ss.Post().GetPostsAfter(model.GetPostsOptions{ChannelId: channelId, PostId: post4.Id, PerPage: 2, CollapsedThreads: true})
require.Nil(t, err)
assert.Equal(t, []string{post5.Id}, postList.Order)
})
})
} }
func testPostStoreGetPostsSince(t *testing.T, ss store.Store) { func testPostStoreGetPostsSince(t *testing.T, ss store.Store) {
@@ -2146,23 +2241,23 @@ func testPostStoreOverwriteMultiple(t *testing.T, ss store.Store) {
}) })
require.Nil(t, err) require.Nil(t, err)
r1, err := ss.Post().Get(o1.Id, false) r1, err := ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro1 := r1.Posts[o1.Id] ro1 := r1.Posts[o1.Id]
r2, err := ss.Post().Get(o2.Id, false) r2, err := ss.Post().Get(o2.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro2 := r2.Posts[o2.Id] ro2 := r2.Posts[o2.Id]
r3, err := ss.Post().Get(o3.Id, false) r3, err := ss.Post().Get(o3.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro3 := r3.Posts[o3.Id] ro3 := r3.Posts[o3.Id]
r4, err := ss.Post().Get(o4.Id, false) r4, err := ss.Post().Get(o4.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro4 := r4.Posts[o4.Id] ro4 := r4.Posts[o4.Id]
r5, err := ss.Post().Get(o5.Id, false) r5, err := ss.Post().Get(o5.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro5 := r5.Posts[o5.Id] ro5 := r5.Posts[o5.Id]
@@ -2188,15 +2283,15 @@ func testPostStoreOverwriteMultiple(t *testing.T, ss store.Store) {
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, -1, errIdx) require.Equal(t, -1, errIdx)
r1, nErr := ss.Post().Get(o1.Id, false) r1, nErr := ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, nErr) require.Nil(t, nErr)
ro1a := r1.Posts[o1.Id] ro1a := r1.Posts[o1.Id]
r2, nErr = ss.Post().Get(o1.Id, false) r2, nErr = ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, nErr) require.Nil(t, nErr)
ro2a := r2.Posts[o2.Id] ro2a := r2.Posts[o2.Id]
r3, nErr = ss.Post().Get(o3.Id, false) r3, nErr = ss.Post().Get(o3.Id, false, false, false)
require.Nil(t, nErr) require.Nil(t, nErr)
ro3a := r3.Posts[o3.Id] ro3a := r3.Posts[o3.Id]
@@ -2218,11 +2313,11 @@ func testPostStoreOverwriteMultiple(t *testing.T, ss store.Store) {
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, -1, errIdx) require.Equal(t, -1, errIdx)
r4, nErr := ss.Post().Get(o4.Id, false) r4, nErr := ss.Post().Get(o4.Id, false, false, false)
require.Nil(t, nErr) require.Nil(t, nErr)
ro4a := r4.Posts[o4.Id] ro4a := r4.Posts[o4.Id]
r5, nErr = ss.Post().Get(o5.Id, false) r5, nErr = ss.Post().Get(o5.Id, false, false, false)
require.Nil(t, nErr) require.Nil(t, nErr)
ro5a := r5.Posts[o5.Id] ro5a := r5.Posts[o5.Id]
@@ -2265,19 +2360,19 @@ func testPostStoreOverwrite(t *testing.T, ss store.Store) {
}) })
require.Nil(t, err) require.Nil(t, err)
r1, err := ss.Post().Get(o1.Id, false) r1, err := ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro1 := r1.Posts[o1.Id] ro1 := r1.Posts[o1.Id]
r2, err := ss.Post().Get(o2.Id, false) r2, err := ss.Post().Get(o2.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro2 := r2.Posts[o2.Id] ro2 := r2.Posts[o2.Id]
r3, err := ss.Post().Get(o3.Id, false) r3, err := ss.Post().Get(o3.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro3 := r3.Posts[o3.Id] ro3 := r3.Posts[o3.Id]
r4, err := ss.Post().Get(o4.Id, false) r4, err := ss.Post().Get(o4.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro4 := r4.Posts[o4.Id] ro4 := r4.Posts[o4.Id]
@@ -2302,15 +2397,15 @@ func testPostStoreOverwrite(t *testing.T, ss store.Store) {
_, err = ss.Post().Overwrite(o3a) _, err = ss.Post().Overwrite(o3a)
require.Nil(t, err) require.Nil(t, err)
r1, err = ss.Post().Get(o1.Id, false) r1, err = ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro1a := r1.Posts[o1.Id] ro1a := r1.Posts[o1.Id]
r2, err = ss.Post().Get(o1.Id, false) r2, err = ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro2a := r2.Posts[o2.Id] ro2a := r2.Posts[o2.Id]
r3, err = ss.Post().Get(o3.Id, false) r3, err = ss.Post().Get(o3.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro3a := r3.Posts[o3.Id] ro3a := r3.Posts[o3.Id]
@@ -2326,7 +2421,7 @@ func testPostStoreOverwrite(t *testing.T, ss store.Store) {
_, err = ss.Post().Overwrite(o4a) _, err = ss.Post().Overwrite(o4a)
require.Nil(t, err) require.Nil(t, err)
r4, err = ss.Post().Get(o4.Id, false) r4, err = ss.Post().Get(o4.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro4a := r4.Posts[o4.Id] ro4a := r4.Posts[o4.Id]
@@ -2357,15 +2452,15 @@ func testPostStoreGetPostsByIds(t *testing.T, ss store.Store) {
o3, err = ss.Post().Save(o3) o3, err = ss.Post().Save(o3)
require.Nil(t, err) require.Nil(t, err)
r1, err := ss.Post().Get(o1.Id, false) r1, err := ss.Post().Get(o1.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro1 := r1.Posts[o1.Id] ro1 := r1.Posts[o1.Id]
r2, err := ss.Post().Get(o2.Id, false) r2, err := ss.Post().Get(o2.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro2 := r2.Posts[o2.Id] ro2 := r2.Posts[o2.Id]
r3, err := ss.Post().Get(o3.Id, false) r3, err := ss.Post().Get(o3.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
ro3 := r3.Posts[o3.Id] ro3 := r3.Posts[o3.Id]
@@ -2472,13 +2567,13 @@ func testPostStorePermanentDeleteBatch(t *testing.T, ss store.Store) {
_, err = ss.Post().PermanentDeleteBatch(2000, 1000) _, err = ss.Post().PermanentDeleteBatch(2000, 1000)
require.Nil(t, err) require.Nil(t, err)
_, err = ss.Post().Get(o1.Id, false) _, err = ss.Post().Get(o1.Id, false, false, false)
require.NotNil(t, err, "Should have not found post 1 after purge") require.NotNil(t, err, "Should have not found post 1 after purge")
_, err = ss.Post().Get(o2.Id, false) _, err = ss.Post().Get(o2.Id, false, false, false)
require.NotNil(t, err, "Should have not found post 2 after purge") require.NotNil(t, err, "Should have not found post 2 after purge")
_, err = ss.Post().Get(o3.Id, false) _, err = ss.Post().Get(o3.Id, false, false, false)
require.Nil(t, err, "Should have not found post 3 after purge") require.Nil(t, err, "Should have not found post 3 after purge")
} }

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

@@ -50,7 +50,7 @@ func testReactionSave(t *testing.T, ss store.Store) {
assert.Equal(t, saved.EmojiName, reaction1.EmojiName, "should've saved reaction emoji_name and returned it") assert.Equal(t, saved.EmojiName, reaction1.EmojiName, "should've saved reaction emoji_name and returned it")
var secondUpdateAt int64 var secondUpdateAt int64
postList, err := ss.Post().Get(reaction1.PostId, false) postList, err := ss.Post().Get(reaction1.PostId, false, false, false)
require.Nil(t, err) require.Nil(t, err)
assert.True(t, postList.Posts[post.Id].HasReactions, "should've set HasReactions = true on post") assert.True(t, postList.Posts[post.Id].HasReactions, "should've set HasReactions = true on post")
@@ -74,7 +74,7 @@ func testReactionSave(t *testing.T, ss store.Store) {
_, nErr = ss.Reaction().Save(reaction2) _, nErr = ss.Reaction().Save(reaction2)
require.Nil(t, nErr) require.Nil(t, nErr)
postList, err = ss.Post().Get(reaction2.PostId, false) postList, err = ss.Post().Get(reaction2.PostId, false, false, false)
require.Nil(t, err) require.Nil(t, err)
assert.NotEqual(t, postList.Posts[post.Id].UpdateAt, secondUpdateAt, "should've marked post as updated even if HasReactions doesn't change") assert.NotEqual(t, postList.Posts[post.Id].UpdateAt, secondUpdateAt, "should've marked post as updated even if HasReactions doesn't change")
@@ -123,7 +123,7 @@ func testReactionDelete(t *testing.T, ss store.Store) {
_, nErr := ss.Reaction().Save(reaction) _, nErr := ss.Reaction().Save(reaction)
require.Nil(t, nErr) require.Nil(t, nErr)
result, err := ss.Post().Get(reaction.PostId, false) result, err := ss.Post().Get(reaction.PostId, false, false, false)
require.Nil(t, err) require.Nil(t, err)
firstUpdateAt := result.Posts[post.Id].UpdateAt firstUpdateAt := result.Posts[post.Id].UpdateAt
@@ -136,7 +136,7 @@ func testReactionDelete(t *testing.T, ss store.Store) {
assert.Empty(t, reactions, "should've deleted reaction") assert.Empty(t, reactions, "should've deleted reaction")
postList, err := ss.Post().Get(post.Id, false) postList, err := ss.Post().Get(post.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
assert.False(t, postList.Posts[post.Id].HasReactions, "should've set HasReactions = false on post") assert.False(t, postList.Posts[post.Id].HasReactions, "should've set HasReactions = false on post")
@@ -297,15 +297,15 @@ func testReactionDeleteAllWithEmojiName(t *testing.T, ss store.Store) {
assert.Empty(t, returned, "should've only removed reactions with emoji name") assert.Empty(t, returned, "should've only removed reactions with emoji name")
// check that the posts are updated // check that the posts are updated
postList, err := ss.Post().Get(post.Id, false) postList, err := ss.Post().Get(post.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
assert.True(t, postList.Posts[post.Id].HasReactions, "post should still have reactions") assert.True(t, postList.Posts[post.Id].HasReactions, "post should still have reactions")
postList, err = ss.Post().Get(post2.Id, false) postList, err = ss.Post().Get(post2.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
assert.True(t, postList.Posts[post2.Id].HasReactions, "post should still have reactions") assert.True(t, postList.Posts[post2.Id].HasReactions, "post should still have reactions")
postList, err = ss.Post().Get(post3.Id, false) postList, err = ss.Post().Get(post3.Id, false, false, false)
require.Nil(t, err) require.Nil(t, err)
assert.False(t, postList.Posts[post3.Id].HasReactions, "post shouldn't have reactions any more") assert.False(t, postList.Posts[post3.Id].HasReactions, "post shouldn't have reactions any more")

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

@@ -69,7 +69,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
newPosts, errIdx, err3 := ss.Post().SaveMultiple([]*model.Post{&o2, &o3, &o4}) newPosts, errIdx, err3 := ss.Post().SaveMultiple([]*model.Post{&o2, &o3, &o4})
olist, _ := ss.Post().Get(otmp.Id, true) olist, _ := ss.Post().Get(otmp.Id, true, false, false)
o1 := olist.Posts[olist.Order[0]] o1 := olist.Posts[olist.Order[0]]
newPosts = append([]*model.Post{o1}, newPosts...) newPosts = append([]*model.Post{o1}, newPosts...)

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

@@ -4444,10 +4444,10 @@ func (s *TimerLayerPostStore) Delete(postId string, time int64, deleteByID strin
return err return err
} }
func (s *TimerLayerPostStore) Get(id string, skipFetchThreads bool) (*model.PostList, error) { func (s *TimerLayerPostStore) Get(id string, skipFetchThreads bool, collapsedThreads bool, collapsedThreadsExtended bool) (*model.PostList, error) {
start := timemodule.Now() start := timemodule.Now()
result, err := s.PostStore.Get(id, skipFetchThreads) result, err := s.PostStore.Get(id, skipFetchThreads, collapsedThreads, collapsedThreadsExtended)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second) elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil { if s.Root.Metrics != nil {
@@ -4476,10 +4476,10 @@ func (s *TimerLayerPostStore) GetDirectPostParentsForExportAfter(limit int, afte
return result, err return result, err
} }
func (s *TimerLayerPostStore) GetEtag(channelId string, allowFromCache bool) string { func (s *TimerLayerPostStore) GetEtag(channelId string, allowFromCache bool, collapsedThreads bool) string {
start := timemodule.Now() start := timemodule.Now()
result := s.PostStore.GetEtag(channelId, allowFromCache) result := s.PostStore.GetEtag(channelId, allowFromCache, collapsedThreads)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second) elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil { if s.Root.Metrics != nil {