diff --git a/api4/commands_test.go b/api4/commands_test.go index 24a2c2714d..59f6b58e0f 100644 --- a/api4/commands_test.go +++ b/api4/commands_test.go @@ -33,7 +33,7 @@ func TestEchoCommand(t *testing.T) { time.Sleep(100 * time.Millisecond) - p1, _, err := client.GetPostsForChannel(channel1.Id, 0, 2, "", false) + p1, _, err := client.GetPostsForChannel(channel1.Id, 0, 2, "", false, false) require.NoError(t, err) require.Len(t, p1.Order, 2, "Echo command failed to send") } @@ -352,7 +352,7 @@ func TestMeCommand(t *testing.T) { time.Sleep(100 * time.Millisecond) - p1, _, err := client.GetPostsForChannel(channel.Id, 0, 2, "", false) + p1, _, err := client.GetPostsForChannel(channel.Id, 0, 2, "", false, false) require.NoError(t, err) require.Len(t, p1.Order, 2, "Command failed to send") @@ -454,7 +454,7 @@ func TestShrugCommand(t *testing.T) { time.Sleep(100 * time.Millisecond) - p1, _, err := client.GetPostsForChannel(channel.Id, 0, 2, "", false) + p1, _, err := client.GetPostsForChannel(channel.Id, 0, 2, "", false, false) require.NoError(t, err) require.Len(t, p1.Order, 2, "Command failed to send") require.Equal(t, `¯\\\_(ツ)\_/¯`, p1.Posts[p1.Order[0]].Message, "invalid shrug response") diff --git a/api4/post.go b/api4/post.go index 4725b4cfaa..479d249967 100644 --- a/api4/post.go +++ b/api4/post.go @@ -183,10 +183,16 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) { skipFetchThreads := r.URL.Query().Get("skipFetchThreads") == "true" collapsedThreads := r.URL.Query().Get("collapsedThreads") == "true" collapsedThreadsExtended := r.URL.Query().Get("collapsedThreadsExtended") == "true" + includeDeleted := r.URL.Query().Get("include_deleted") == "true" channelId := c.Params.ChannelId page := c.Params.Page perPage := c.Params.PerPage + if !c.IsSystemAdmin() && includeDeleted { + c.SetPermissionError(model.PermissionReadDeletedPosts) + return + } + if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channelId, model.PermissionReadChannel) { c.SetPermissionError(model.PermissionReadChannel) return @@ -217,7 +223,7 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } - list, err = c.App.GetPostsAfterPost(model.GetPostsOptions{ChannelId: channelId, PostId: afterPost, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, UserId: c.AppContext.Session().UserId}) + list, err = c.App.GetPostsAfterPost(model.GetPostsOptions{ChannelId: channelId, PostId: afterPost, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, UserId: c.AppContext.Session().UserId, IncludeDeleted: includeDeleted}) } else if beforePost != "" { etag = c.App.GetPostsEtag(channelId, collapsedThreads) @@ -225,7 +231,7 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } - list, err = c.App.GetPostsBeforePost(model.GetPostsOptions{ChannelId: channelId, PostId: beforePost, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, CollapsedThreadsExtended: collapsedThreadsExtended, UserId: c.AppContext.Session().UserId}) + list, err = c.App.GetPostsBeforePost(model.GetPostsOptions{ChannelId: channelId, PostId: beforePost, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, CollapsedThreadsExtended: collapsedThreadsExtended, UserId: c.AppContext.Session().UserId, IncludeDeleted: includeDeleted}) } else { etag = c.App.GetPostsEtag(channelId, collapsedThreads) @@ -233,7 +239,7 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } - list, err = c.App.GetPostsPage(model.GetPostsOptions{ChannelId: channelId, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, CollapsedThreadsExtended: collapsedThreadsExtended, UserId: c.AppContext.Session().UserId}) + list, err = c.App.GetPostsPage(model.GetPostsOptions{ChannelId: channelId, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, CollapsedThreadsExtended: collapsedThreadsExtended, UserId: c.AppContext.Session().UserId, IncludeDeleted: includeDeleted}) } if err != nil { diff --git a/api4/post_test.go b/api4/post_test.go index 6447fb890b..31f1185ea9 100644 --- a/api4/post_test.go +++ b/api4/post_test.go @@ -1068,17 +1068,17 @@ func TestGetPostsForChannel(t *testing.T) { post4 := th.CreatePost() th.TestForAllClients(t, func(t *testing.T, c *model.Client4) { - posts, resp, err := c.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "", false) + posts, resp, err := c.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "", false, false) require.NoError(t, err) require.Equal(t, post4.Id, posts.Order[0], "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, post1.Id, posts.Order[3], "wrong order") - posts, resp, _ = c.GetPostsForChannel(th.BasicChannel.Id, 0, 3, resp.Etag, false) + posts, resp, _ = c.GetPostsForChannel(th.BasicChannel.Id, 0, 3, resp.Etag, false, false) CheckEtag(t, posts, resp) - posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 0, 3, "", false) + posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 0, 3, "", false, false) require.NoError(t, err) require.Len(t, posts.Order, 3, "wrong number returned") @@ -1087,11 +1087,11 @@ func TestGetPostsForChannel(t *testing.T) { _, ok = posts.Posts[post1.Id] require.True(t, ok, "missing root post") - posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 1, 1, "", false) + posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 1, 1, "", false, false) require.NoError(t, err) require.Equal(t, post3.Id, posts.Order[0], "wrong order") - posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 10000, 10000, "", false) + posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 10000, 10000, "", false, false) require.NoError(t, err) require.Empty(t, posts.Order, "should be no posts") }) @@ -1121,21 +1121,21 @@ func TestGetPostsForChannel(t *testing.T) { require.True(t, f, "missing post") } - _, resp, err := c.GetPostsForChannel("", 0, 60, "", false) + _, resp, err := c.GetPostsForChannel("", 0, 60, "", false, false) require.Error(t, err) CheckBadRequestStatus(t, resp) - _, resp, err = c.GetPostsForChannel("junk", 0, 60, "", false) + _, resp, err = c.GetPostsForChannel("junk", 0, 60, "", false, false) require.Error(t, err) CheckBadRequestStatus(t, resp) }) - _, resp, err := client.GetPostsForChannel(model.NewId(), 0, 60, "", false) + _, resp, err := client.GetPostsForChannel(model.NewId(), 0, 60, "", false, false) require.Error(t, err) CheckForbiddenStatus(t, resp) client.Logout() - _, resp, err = client.GetPostsForChannel(model.NewId(), 0, 60, "", false) + _, resp, err = client.GetPostsForChannel(model.NewId(), 0, 60, "", false, false) require.Error(t, err) CheckUnauthorizedStatus(t, resp) @@ -1151,12 +1151,12 @@ func TestGetPostsForChannel(t *testing.T) { var posts *model.PostList th.TestForAllClients(t, func(t *testing.T, c *model.Client4) { // get the system post IDs posted before the created posts above - posts, _, err = c.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "", false) + posts, _, err = c.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "", false, false) require.NoError(t, err) systemPostId1 := posts.Order[1] // similar to '/posts' - posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "", false) + posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "", false, false) require.NoError(t, err) require.Len(t, posts.Order, 12, "expected 12 posts") require.Equal(t, post10.Id, posts.Order[0], "posts not in order") @@ -1165,7 +1165,7 @@ func TestGetPostsForChannel(t *testing.T) { require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") // similar to '/posts?per_page=3' - posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 0, 3, "", false) + posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 0, 3, "", false, false) require.NoError(t, err) require.Len(t, posts.Order, 3, "expected 3 posts") require.Equal(t, post10.Id, posts.Order[0], "posts not in order") @@ -1174,7 +1174,7 @@ func TestGetPostsForChannel(t *testing.T) { require.Equal(t, post7.Id, posts.PrevPostId, "should return post7.Id as PrevPostId") // similar to '/posts?per_page=3&page=1' - posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 1, 3, "", false) + posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 1, 3, "", false, false) require.NoError(t, err) require.Len(t, posts.Order, 3, "expected 3 posts") require.Equal(t, post7.Id, posts.Order[0], "posts not in order") @@ -1183,7 +1183,7 @@ func TestGetPostsForChannel(t *testing.T) { require.Equal(t, post4.Id, posts.PrevPostId, "should return post4.Id as PrevPostId") // similar to '/posts?per_page=3&page=2' - posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 2, 3, "", false) + posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 2, 3, "", false, false) require.NoError(t, err) require.Len(t, posts.Order, 3, "expected 3 posts") require.Equal(t, post4.Id, posts.Order[0], "posts not in order") @@ -1192,7 +1192,7 @@ func TestGetPostsForChannel(t *testing.T) { require.Equal(t, post1.Id, posts.PrevPostId, "should return post1.Id as PrevPostId") // similar to '/posts?per_page=3&page=3' - posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 3, 3, "", false) + posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 3, 3, "", false, false) require.NoError(t, err) require.Len(t, posts.Order, 3, "expected 3 posts") require.Equal(t, post1.Id, posts.Order[0], "posts not in order") @@ -1201,7 +1201,7 @@ func TestGetPostsForChannel(t *testing.T) { require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") // similar to '/posts?per_page=3&page=4' - posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 4, 3, "", false) + posts, _, err = c.GetPostsForChannel(th.BasicChannel.Id, 4, 3, "", false, false) require.NoError(t, err) require.Empty(t, posts.Order, "should return 0 post") require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") @@ -1220,15 +1220,37 @@ func TestGetPostsForChannel(t *testing.T) { }) // the endpoint should work fine when viewing archived channels is enabled - _, _, err = c.GetPostsForChannel(channel.Id, 0, 10, "", false) + _, _, err = c.GetPostsForChannel(channel.Id, 0, 10, "", false, false) require.NoError(t, err) // the endpoint should return forbidden if viewing archived channels is disabled th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.ExperimentalViewArchivedChannels = false }) - _, resp, err = c.GetPostsForChannel(channel.Id, 0, 10, "", false) + _, resp, err = c.GetPostsForChannel(channel.Id, 0, 10, "", false, false) require.Error(t, err) CheckForbiddenStatus(t, resp) }, "Should forbid to retrieve posts if the channel is archived and users are not allowed to view archived messages") + + client.DeletePost(post10.Id) + client.DeletePost(post8.Id) + + // include deleted posts for non-admin users. + _, resp, err = client.GetPostsForChannel(th.BasicChannel.Id, 0, 100, "", false, true) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + + th.TestForSystemAdminAndLocal(t, func(t *testing.T, c *model.Client4) { + // include deleted posts for admin users. + posts, resp, err = c.GetPostsForChannel(th.BasicChannel.Id, 0, 100, "", false, true) + require.NoError(t, err) + CheckOKStatus(t, resp) + require.Len(t, posts.Order, 12, "expected 12 posts") + + // not include deleted posts for admin users. + posts, resp, err = c.GetPostsForChannel(th.BasicChannel.Id, 0, 100, "", false, false) + require.NoError(t, err) + CheckOKStatus(t, resp) + require.Len(t, posts.Order, 10, "expected 10 posts") + }) } func TestGetFlaggedPostsForUser(t *testing.T) { @@ -1440,7 +1462,7 @@ func TestGetPostsBefore(t *testing.T) { post4 := th.CreatePost() post5 := th.CreatePost() - posts, _, err := client.GetPostsBefore(th.BasicChannel.Id, post3.Id, 0, 100, "", false) + posts, _, err := client.GetPostsBefore(th.BasicChannel.Id, post3.Id, 0, 100, "", false, false) require.NoError(t, err) found := make([]bool, 2) @@ -1462,18 +1484,18 @@ func TestGetPostsBefore(t *testing.T) { require.Equal(t, post3.Id, posts.NextPostId, "should match NextPostId") require.Equal(t, "", posts.PrevPostId, "should match empty PrevPostId") - posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post4.Id, 1, 1, "", false) + posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post4.Id, 1, 1, "", false, false) require.NoError(t, err) 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, post3.Id, posts.NextPostId, "should match NextPostId") require.Equal(t, post1.Id, posts.PrevPostId, "should match PrevPostId") - _, resp, err := client.GetPostsBefore(th.BasicChannel.Id, "junk", 1, 1, "", false) + _, resp, err := client.GetPostsBefore(th.BasicChannel.Id, "junk", 1, 1, "", false, false) require.Error(t, err) CheckBadRequestStatus(t, resp) - posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post5.Id, 0, 3, "", false) + posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post5.Id, 0, 3, "", false, false) require.NoError(t, err) require.Len(t, posts.Posts, 3, "should match length of posts returned") require.Equal(t, post4.Id, posts.Order[0], "should match returned post") @@ -1482,12 +1504,12 @@ func TestGetPostsBefore(t *testing.T) { require.Equal(t, post1.Id, posts.PrevPostId, "should match PrevPostId") // get the system post IDs posted before the created posts above - posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "", false) + posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "", false, false) require.NoError(t, err) systemPostId2 := posts.Order[0] systemPostId1 := posts.Order[1] - posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post5.Id, 1, 3, "", false) + posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post5.Id, 1, 3, "", false, false) require.NoError(t, err) require.Len(t, posts.Posts, 3, "should match length of posts returned") require.Equal(t, post1.Id, posts.Order[0], "should match returned post") @@ -1502,10 +1524,10 @@ func TestGetPostsBefore(t *testing.T) { th.CreatePost() // post7 post8 := th.CreatePost() post9 := th.CreatePost() - th.CreatePost() // post10 + post10 := th.CreatePost() // post10 // similar to '/posts?before=post9' - posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 0, 60, "", false) + posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 0, 60, "", false, false) require.NoError(t, err) require.Len(t, posts.Order, 10, "expected 10 posts") require.Equal(t, post8.Id, posts.Order[0], "posts not in order") @@ -1514,7 +1536,7 @@ func TestGetPostsBefore(t *testing.T) { require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") // similar to '/posts?before=post9&per_page=3' - posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 0, 3, "", false) + posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 0, 3, "", false, false) require.NoError(t, err) require.Len(t, posts.Order, 3, "expected 3 posts") require.Equal(t, post8.Id, posts.Order[0], "posts not in order") @@ -1523,7 +1545,7 @@ func TestGetPostsBefore(t *testing.T) { require.Equal(t, post5.Id, posts.PrevPostId, "should return post5.Id as PrevPostId") // similar to '/posts?before=post9&per_page=3&page=1' - posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 1, 3, "", false) + posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 1, 3, "", false, false) require.NoError(t, err) require.Len(t, posts.Order, 3, "expected 3 posts") require.Equal(t, post5.Id, posts.Order[0], "posts not in order") @@ -1532,7 +1554,7 @@ func TestGetPostsBefore(t *testing.T) { require.Equal(t, post2.Id, posts.PrevPostId, "should return post2.Id as PrevPostId") // similar to '/posts?before=post9&per_page=3&page=2' - posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 2, 3, "", false) + posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 2, 3, "", false, false) require.NoError(t, err) require.Len(t, posts.Order, 3, "expected 3 posts") require.Equal(t, post2.Id, posts.Order[0], "posts not in order") @@ -1541,7 +1563,7 @@ func TestGetPostsBefore(t *testing.T) { require.Equal(t, systemPostId1, posts.PrevPostId, "should return systemPostId1 as PrevPostId") // similar to '/posts?before=post1&per_page=3' - posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 3, "", false) + posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 3, "", false, false) require.NoError(t, err) require.Len(t, posts.Order, 2, "expected 2 posts") require.Equal(t, systemPostId2, posts.Order[0], "posts not in order") @@ -1550,14 +1572,14 @@ func TestGetPostsBefore(t *testing.T) { require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") // similar to '/posts?before=systemPostId1' - posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, systemPostId1, 0, 60, "", false) + posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, systemPostId1, 0, 60, "", false, false) require.NoError(t, err) require.Empty(t, posts.Order, "should return 0 post") require.Equal(t, systemPostId1, posts.NextPostId, "should return systemPostId1 as NextPostId") require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") // similar to '/posts?before=systemPostId1&per_page=60&page=1' - posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, systemPostId1, 1, 60, "", false) + posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, systemPostId1, 1, 60, "", false, false) require.NoError(t, err) require.Empty(t, posts.Order, "should return 0 posts") require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") @@ -1565,11 +1587,33 @@ func TestGetPostsBefore(t *testing.T) { // similar to '/posts?before=non-existent-post' nonExistentPostId := model.NewId() - posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, nonExistentPostId, 0, 60, "", false) + posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, nonExistentPostId, 0, 60, "", false, false) require.NoError(t, err) require.Empty(t, posts.Order, "should return 0 post") require.Equal(t, nonExistentPostId, posts.NextPostId, "should return nonExistentPostId as NextPostId") require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") + + client.DeletePost(post9.Id) + client.DeletePost(post8.Id) + + // include deleted posts for non-admin users. + _, resp, err = client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 0, 60, "", false, true) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + + th.TestForSystemAdminAndLocal(t, func(t *testing.T, c *model.Client4) { + // include deleted posts for admin users. + posts, resp, err = c.GetPostsBefore(th.BasicChannel.Id, post10.Id, 0, 60, "", false, true) + require.NoError(t, err) + CheckOKStatus(t, resp) + require.Len(t, posts.Order, 11, "expected 11 posts") + + // not include deleted posts for admin users. + posts, resp, err = c.GetPostsBefore(th.BasicChannel.Id, post10.Id, 0, 60, "", false, false) + require.NoError(t, err) + CheckOKStatus(t, resp) + require.Len(t, posts.Order, 9, "expected 9 posts") + }) } func TestGetPostsAfter(t *testing.T) { @@ -1583,7 +1627,7 @@ func TestGetPostsAfter(t *testing.T) { post4 := th.CreatePost() post5 := th.CreatePost() - posts, _, err := client.GetPostsAfter(th.BasicChannel.Id, post3.Id, 0, 100, "", false) + posts, _, err := client.GetPostsAfter(th.BasicChannel.Id, post3.Id, 0, 100, "", false, false) require.NoError(t, err) found := make([]bool, 2) @@ -1603,18 +1647,18 @@ func TestGetPostsAfter(t *testing.T) { require.Equal(t, "", posts.NextPostId, "should match empty NextPostId") require.Equal(t, post3.Id, posts.PrevPostId, "should match PrevPostId") - posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 1, 1, "", false) + posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 1, 1, "", false, false) require.NoError(t, err) 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, post5.Id, posts.NextPostId, "should match NextPostId") require.Equal(t, post3.Id, posts.PrevPostId, "should match PrevPostId") - _, resp, err := client.GetPostsAfter(th.BasicChannel.Id, "junk", 1, 1, "", false) + _, resp, err := client.GetPostsAfter(th.BasicChannel.Id, "junk", 1, 1, "", false, false) require.Error(t, err) CheckBadRequestStatus(t, resp) - posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post1.Id, 0, 3, "", false) + posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post1.Id, 0, 3, "", false, false) require.NoError(t, err) require.Len(t, posts.Posts, 3, "should match length of posts returned") require.Equal(t, post4.Id, posts.Order[0], "should match returned post") @@ -1622,7 +1666,7 @@ func TestGetPostsAfter(t *testing.T) { require.Equal(t, post5.Id, posts.NextPostId, "should match NextPostId") require.Equal(t, post1.Id, posts.PrevPostId, "should match PrevPostId") - posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post1.Id, 1, 3, "", false) + posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post1.Id, 1, 3, "", false, false) require.NoError(t, err) require.Len(t, posts.Posts, 1, "should match length of posts returned") require.Equal(t, post5.Id, posts.Order[0], "should match returned post") @@ -1638,7 +1682,7 @@ func TestGetPostsAfter(t *testing.T) { post10 := th.CreatePost() // similar to '/posts?after=post2' - posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 0, 60, "", false) + posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 0, 60, "", false, false) require.NoError(t, err) require.Len(t, posts.Order, 8, "expected 8 posts") require.Equal(t, post10.Id, posts.Order[0], "should match order") @@ -1647,7 +1691,7 @@ func TestGetPostsAfter(t *testing.T) { require.Equal(t, post2.Id, posts.PrevPostId, "should return post2.Id as PrevPostId") // similar to '/posts?after=post2&per_page=3' - posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 0, 3, "", false) + posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 0, 3, "", false, false) require.NoError(t, err) require.Len(t, posts.Order, 3, "expected 3 posts") require.Equal(t, post5.Id, posts.Order[0], "should match order") @@ -1656,7 +1700,7 @@ func TestGetPostsAfter(t *testing.T) { require.Equal(t, post2.Id, posts.PrevPostId, "should return post2.Id as PrevPostId") // similar to '/posts?after=post2&per_page=3&page=1' - posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 1, 3, "", false) + posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 1, 3, "", false, false) require.NoError(t, err) require.Len(t, posts.Order, 3, "expected 3 posts") require.Equal(t, post8.Id, posts.Order[0], "should match order") @@ -1665,7 +1709,7 @@ func TestGetPostsAfter(t *testing.T) { require.Equal(t, post5.Id, posts.PrevPostId, "should return post5.Id as PrevPostId") // similar to '/posts?after=post2&per_page=3&page=2' - posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 2, 3, "", false) + posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 2, 3, "", false, false) require.NoError(t, err) require.Len(t, posts.Order, 2, "expected 2 posts") require.Equal(t, post10.Id, posts.Order[0], "should match order") @@ -1674,14 +1718,14 @@ func TestGetPostsAfter(t *testing.T) { require.Equal(t, post8.Id, posts.PrevPostId, "should return post8.Id as PrevPostId") // similar to '/posts?after=post10' - posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post10.Id, 0, 60, "", false) + posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post10.Id, 0, 60, "", false, false) require.NoError(t, err) require.Empty(t, posts.Order, "should return 0 post") require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") require.Equal(t, post10.Id, posts.PrevPostId, "should return post10.Id as PrevPostId") // similar to '/posts?after=post10&page=1' - posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post10.Id, 1, 60, "", false) + posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, post10.Id, 1, 60, "", false, false) require.NoError(t, err) require.Empty(t, posts.Order, "should return 0 post") require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") @@ -1689,11 +1733,33 @@ func TestGetPostsAfter(t *testing.T) { // similar to '/posts?after=non-existent-post' nonExistentPostId := model.NewId() - posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, nonExistentPostId, 0, 60, "", false) + posts, _, err = client.GetPostsAfter(th.BasicChannel.Id, nonExistentPostId, 0, 60, "", false, false) require.NoError(t, err) require.Empty(t, posts.Order, "should return 0 post") require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") require.Equal(t, nonExistentPostId, posts.PrevPostId, "should return nonExistentPostId as PrevPostId") + + client.DeletePost(post10.Id) + client.DeletePost(post9.Id) + + // include deleted posts for non-admin users. + _, resp, err = client.GetPostsAfter(th.BasicChannel.Id, post1.Id, 0, 60, "", false, true) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + + th.TestForSystemAdminAndLocal(t, func(t *testing.T, c *model.Client4) { + // include deleted posts for admin users. + posts, resp, err = c.GetPostsAfter(th.BasicChannel.Id, post1.Id, 0, 60, "", false, true) + require.NoError(t, err) + CheckOKStatus(t, resp) + require.Len(t, posts.Order, 9, "expected 9 posts") + + // not include deleted posts for admin users. + posts, resp, err = c.GetPostsAfter(th.BasicChannel.Id, post1.Id, 0, 60, "", false, false) + require.NoError(t, err) + CheckOKStatus(t, resp) + require.Len(t, posts.Order, 7, "expected 7 posts") + }) } func TestGetPostsForChannelAroundLastUnread(t *testing.T) { @@ -1798,7 +1864,7 @@ func TestGetPostsForChannelAroundLastUnread(t *testing.T) { 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 - posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "", false) + posts, _, err = client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "", false, false) require.NoError(t, err) systemPost0 := posts.Posts[posts.Order[0]] postIdNames[systemPost0.Id] = "system post 0" diff --git a/api4/team_test.go b/api4/team_test.go index a8b7a45702..e4dcc431c2 100644 --- a/api4/team_test.go +++ b/api4/team_test.go @@ -3028,7 +3028,7 @@ func TestImportTeam(t *testing.T) { require.NoError(t, err) require.Equal(t, importedChannel.Name, "general", "names did not match expected: general") - posts, _, err := th.SystemAdminClient.GetPostsForChannel(importedChannel.Id, 0, 60, "", false) + posts, _, err := th.SystemAdminClient.GetPostsForChannel(importedChannel.Id, 0, 60, "", false, false) require.NoError(t, err) 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") }) diff --git a/model/client4.go b/model/client4.go index 9e069fa535..f00852a77e 100644 --- a/model/client4.go +++ b/model/client4.go @@ -3925,11 +3925,15 @@ func (c *Client4) GetPostThreadWithOpts(postID string, etag string, opts GetPost } // 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, collapsedThreads bool) (*PostList, *Response, error) { +func (c *Client4) GetPostsForChannel(channelId string, page, perPage int, etag string, collapsedThreads bool, includeDeleted bool) (*PostList, *Response, error) { query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) if collapsedThreads { query += "&collapsedThreads=true" } + + if includeDeleted { + query += "&include_deleted=true" + } r, err := c.DoAPIGet(c.channelRoute(channelId)+"/posts"+query, etag) if err != nil { return nil, BuildResponse(r), err @@ -4050,11 +4054,14 @@ func (c *Client4) GetPostsSince(channelId string, time int64, collapsedThreads b } // 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, collapsedThreads bool) (*PostList, *Response, error) { +func (c *Client4) GetPostsAfter(channelId, postId string, page, perPage int, etag string, collapsedThreads bool, includeDeleted bool) (*PostList, *Response, error) { query := fmt.Sprintf("?page=%v&per_page=%v&after=%v", page, perPage, postId) if collapsedThreads { query += "&collapsedThreads=true" } + if includeDeleted { + query += "&include_deleted=true" + } r, err := c.DoAPIGet(c.channelRoute(channelId)+"/posts"+query, etag) if err != nil { return nil, BuildResponse(r), err @@ -4071,11 +4078,14 @@ func (c *Client4) GetPostsAfter(channelId, postId string, page, perPage int, eta } // 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, collapsedThreads bool) (*PostList, *Response, error) { +func (c *Client4) GetPostsBefore(channelId, postId string, page, perPage int, etag string, collapsedThreads bool, includeDeleted bool) (*PostList, *Response, error) { query := fmt.Sprintf("?page=%v&per_page=%v&before=%v", page, perPage, postId) if collapsedThreads { query += "&collapsedThreads=true" } + if includeDeleted { + query += "&include_deleted=true" + } r, err := c.DoAPIGet(c.channelRoute(channelId)+"/posts"+query, etag) if err != nil { return nil, BuildResponse(r), err diff --git a/model/permission.go b/model/permission.go index bf1f40238c..65f8edaca8 100644 --- a/model/permission.go +++ b/model/permission.go @@ -73,6 +73,7 @@ var PermissionDeleteOthersEmojis *Permission var PermissionCreatePost *Permission var PermissionCreatePostPublic *Permission var PermissionCreatePostEphemeral *Permission +var PermissionReadDeletedPosts *Permission var PermissionEditPost *Permission var PermissionEditOthersPosts *Permission var PermissionDeletePost *Permission @@ -708,6 +709,12 @@ func initializePermissions() { "authentication.permissions.create_post_ephemeral.description", PermissionScopeChannel, } + PermissionReadDeletedPosts = &Permission{ + "read_deleted_posts", + "authentication.permissions.read_deleted_posts.name", + "authentication.permissions.read_deleted_posts.description", + PermissionScopeChannel, + } PermissionEditPost = &Permission{ "edit_post", "authentication.permissions.edit_post.name", @@ -2302,6 +2309,7 @@ func initializePermissions() { PermissionCreatePost, PermissionCreatePostPublic, PermissionCreatePostEphemeral, + PermissionReadDeletedPosts, PermissionEditPost, PermissionEditOthersPosts, PermissionDeletePost, diff --git a/model/post.go b/model/post.go index f0ebcfbea2..4916779b52 100644 --- a/model/post.go +++ b/model/post.go @@ -304,6 +304,7 @@ type GetPostsOptions struct { FromPost string // PostId after which to send the items FromCreateAt int64 // CreateAt after which to send the items Direction string // Only accepts up|down. Indicates the order in which to send the items. + IncludeDeleted bool } type PostCountOptions struct { diff --git a/store/sqlstore/post_store.go b/store/sqlstore/post_store.go index 2f3fd2b6b9..747b9d0f81 100644 --- a/store/sqlstore/post_store.go +++ b/store/sqlstore/post_store.go @@ -1168,13 +1168,13 @@ func (s *SqlPostStore) GetPosts(options model.GetPostsOptions, _ bool, sanitizeO rpc := make(chan store.StoreResult, 1) go func() { - posts, err := s.getRootPosts(options.ChannelId, offset, options.PerPage, options.SkipFetchThreads) + posts, err := s.getRootPosts(options.ChannelId, offset, options.PerPage, options.SkipFetchThreads, options.IncludeDeleted) rpc <- store.StoreResult{Data: posts, NErr: err} close(rpc) }() cpc := make(chan store.StoreResult, 1) go func() { - posts, err := s.getParentsPosts(options.ChannelId, offset, options.PerPage, options.SkipFetchThreads) + posts, err := s.getParentsPosts(options.ChannelId, offset, options.PerPage, options.SkipFetchThreads, options.IncludeDeleted) cpc <- store.StoreResult{Data: posts, NErr: err} close(cpc) }() @@ -1440,13 +1440,18 @@ func (s *SqlPostStore) getPostsAround(before bool, options model.GetPostsOptions ) } query := s.getQueryBuilder().Select(columns...) - replyCountSubQuery := s.getQueryBuilder().Select("COUNT(*)").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(*)").From("Posts").Where(sq.Expr("Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END)")) conditions := sq.And{ sq.Expr(`CreateAt `+direction+` (SELECT CreateAt FROM Posts WHERE Id = ?)`, options.PostId), sq.Eq{"p.ChannelId": options.ChannelId}, - sq.Eq{"p.DeleteAt": int(0)}, } + + if !options.IncludeDeleted { + replyCountSubQuery = replyCountSubQuery.Where(sq.Expr("Posts.DeleteAt = 0")) + conditions = append(conditions, sq.Eq{"p.DeleteAt": int(0)}) + } + if options.CollapsedThreads { conditions = append(conditions, sq.Eq{"RootId": ""}) query = query.LeftJoin("Threads ON Threads.PostId = p.Id").LeftJoin("ThreadMemberships ON ThreadMemberships.PostId = p.Id AND ThreadMemberships.UserId=?", options.UserId) @@ -1492,10 +1497,13 @@ func (s *SqlPostStore) getPostsAround(before bool, options model.GetPostsOptions Where(sq.And{ idQuery, sq.Eq{"p.ChannelId": options.ChannelId}, - sq.Eq{"p.DeleteAt": 0}, }). OrderBy("CreateAt DESC") + if !options.IncludeDeleted { + rootQuery = rootQuery.Where(sq.Eq{"p.DeleteAt": 0}) + } + rootQueryString, rootArgs, nErr := rootQuery.ToSql() if nErr != nil { @@ -1620,14 +1628,21 @@ func (s *SqlPostStore) GetPostAfterTime(channelId string, time int64, collapsedT return &post, nil } -func (s *SqlPostStore) getRootPosts(channelId string, offset int, limit int, skipFetchThreads bool) ([]*model.Post, error) { +func (s *SqlPostStore) getRootPosts(channelId string, offset int, limit int, skipFetchThreads bool, includeDeleted bool) ([]*model.Post, error) { posts := []*model.Post{} var fetchQuery string if skipFetchThreads { - fetchQuery = "SELECT p.*, (SELECT COUNT(*) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0) as ReplyCount FROM Posts p WHERE p.ChannelId = ? AND p.DeleteAt = 0 ORDER BY p.CreateAt DESC LIMIT ? OFFSET ?" + fetchQuery = "SELECT p.*, (SELECT COUNT(*) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END)) as ReplyCount FROM Posts p WHERE p.ChannelId = ? ORDER BY p.CreateAt DESC LIMIT ? OFFSET ?" + if !includeDeleted { + fetchQuery = "SELECT p.*, (SELECT COUNT(*) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0) as ReplyCount FROM Posts p WHERE p.ChannelId = ? AND p.DeleteAt = 0 ORDER BY p.CreateAt DESC LIMIT ? OFFSET ?" + } } else { - fetchQuery = "SELECT * FROM Posts WHERE Posts.ChannelId = ? AND Posts.DeleteAt = 0 ORDER BY Posts.CreateAt DESC LIMIT ? OFFSET ?" + fetchQuery = "SELECT * FROM Posts WHERE Posts.ChannelId = ? ORDER BY Posts.CreateAt DESC LIMIT ? OFFSET ?" + if !includeDeleted { + fetchQuery = "SELECT * FROM Posts WHERE Posts.ChannelId = ? AND Posts.DeleteAt = 0 ORDER BY Posts.CreateAt DESC LIMIT ? OFFSET ?" + } } + err := s.GetReplicaX().Select(&posts, fetchQuery, channelId, limit, offset) if err != nil { return nil, errors.Wrap(err, "failed to find Posts") @@ -1635,9 +1650,14 @@ func (s *SqlPostStore) getRootPosts(channelId string, offset int, limit int, ski return posts, nil } -func (s *SqlPostStore) getParentsPosts(channelId string, offset int, limit int, skipFetchThreads bool) ([]*model.Post, error) { +func (s *SqlPostStore) getParentsPosts(channelId string, offset int, limit int, skipFetchThreads bool, includeDeleted bool) ([]*model.Post, error) { if s.DriverName() == model.DatabaseDriverPostgres { - return s.getParentsPostsPostgreSQL(channelId, offset, limit, skipFetchThreads) + return s.getParentsPostsPostgreSQL(channelId, offset, limit, skipFetchThreads, includeDeleted) + } + + deleteAtCondition := "AND DeleteAt = 0" + if includeDeleted { + deleteAtCondition = "" } // query parent Ids first @@ -1651,9 +1671,8 @@ func (s *SqlPostStore) getParentsPosts(channelId string, offset int, limit int, FROM Posts WHERE - Posts.ChannelId = ? - AND Posts.DeleteAt = 0 - ORDER BY Posts.CreateAt DESC + ChannelId = ? ` + deleteAtCondition + ` + ORDER BY CreateAt DESC LIMIT ? OFFSET ?) q WHERE q.RootId != ''` @@ -1669,7 +1688,11 @@ func (s *SqlPostStore) getParentsPosts(channelId string, offset int, limit int, var where sq.Sqlizer where = sq.Eq{"p.Id": roots} if skipFetchThreads { - cols = append(cols, "(SELECT COUNT(*) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0) as ReplyCount") + col := "(SELECT COUNT(*) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END)) as ReplyCount" + if !includeDeleted { + col = "(SELECT COUNT(*) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0) as ReplyCount" + } + cols = append(cols, col) } else { where = sq.Or{ where, @@ -1683,10 +1706,13 @@ func (s *SqlPostStore) getParentsPosts(channelId string, offset int, limit int, Where(sq.And{ where, sq.Eq{"p.ChannelId": channelId}, - sq.Eq{"p.DeleteAt": 0}, }). OrderBy("p.CreateAt") + if !includeDeleted { + query = query.Where(sq.Eq{"p.DeleteAt": 0}) + } + sql, args, err := query.ToSql() if err != nil { return nil, errors.Wrap(err, "ParentPosts_Tosql") @@ -1700,15 +1726,25 @@ func (s *SqlPostStore) getParentsPosts(channelId string, offset int, limit int, return posts, nil } -func (s *SqlPostStore) getParentsPostsPostgreSQL(channelId string, offset int, limit int, skipFetchThreads bool) ([]*model.Post, error) { +func (s *SqlPostStore) getParentsPostsPostgreSQL(channelId string, offset int, limit int, skipFetchThreads bool, includeDeleted bool) ([]*model.Post, error) { posts := []*model.Post{} replyCountQuery := "" onStatement := "q1.RootId = q2.Id" if skipFetchThreads { - replyCountQuery = ` ,(SELECT COUNT(*) FROM Posts WHERE Posts.RootId = (CASE WHEN q2.RootId = '' THEN q2.Id ELSE q2.RootId END) AND Posts.DeleteAt = 0) as ReplyCount` + replyCountQuery = ` ,(SELECT COUNT(*) FROM Posts WHERE Posts.RootId = (CASE WHEN q2.RootId = '' THEN q2.Id ELSE q2.RootId END)) as ReplyCount` + if !includeDeleted { + replyCountQuery = ` ,(SELECT COUNT(*) FROM Posts WHERE Posts.RootId = (CASE WHEN q2.RootId = '' THEN q2.Id ELSE q2.RootId END) AND Posts.DeleteAt = 0) as ReplyCount` + } } else { onStatement += " OR q1.RootId = q2.RootId" } + + deleteAtQueryCondition := "AND q2.DeleteAt = 0" + deleteAtSubQueryCondition := "AND Posts.DeleteAt = 0" + if includeDeleted { + deleteAtQueryCondition, deleteAtSubQueryCondition = "", "" + } + err := s.GetReplicaX().Select(&posts, `SELECT q2.*`+replyCountQuery+` FROM @@ -1722,15 +1758,13 @@ func (s *SqlPostStore) getParentsPostsPostgreSQL(channelId string, offset int, l FROM Posts WHERE - Posts.ChannelId = ? - AND Posts.DeleteAt = 0 + Posts.ChannelId = ? `+deleteAtSubQueryCondition+` ORDER BY Posts.CreateAt DESC LIMIT ? OFFSET ?) q3 WHERE q3.RootId != '') q1 ON `+onStatement+` WHERE - q2.ChannelId = ? - AND q2.DeleteAt = 0 + q2.ChannelId = ? `+deleteAtQueryCondition+` ORDER BY q2.CreateAt`, channelId, limit, offset, channelId) if err != nil { return nil, errors.Wrapf(err, "failed to find Posts with channelId=%s", channelId) diff --git a/store/storetest/post_store.go b/store/storetest/post_store.go index 41b8c138d3..dd1ba40ae4 100644 --- a/store/storetest/post_store.go +++ b/store/storetest/post_store.go @@ -1946,6 +1946,73 @@ func testPostStoreGetPosts(t *testing.T, ss store.Store) { assert.Equal(t, int64(1), postList.Posts[post5.Id].ReplyCount) assert.Equal(t, int64(1), postList.Posts[post6.Id].ReplyCount) }) + + t.Run("should return all posts in a channel included deleted posts", func(t *testing.T) { + err := ss.Post().Delete(post1.Id, 1, userId) + require.NoError(t, err) + + postList, err := ss.Post().GetPosts(model.GetPostsOptions{ChannelId: channelId, Page: 0, PerPage: 30, SkipFetchThreads: false, IncludeDeleted: true}, false, map[string]bool{}) + require.NoError(t, err) + + assert.Equal(t, []string{ + post6.Id, + post5.Id, + post4.Id, + post3.Id, + post2.Id, + post1.Id, + }, postList.Order) + + assert.Len(t, postList.Posts, 6) + assert.NotNil(t, postList.Posts[post1.Id]) + assert.NotNil(t, postList.Posts[post2.Id]) + assert.NotNil(t, postList.Posts[post3.Id]) + assert.NotNil(t, postList.Posts[post4.Id]) + assert.NotNil(t, postList.Posts[post5.Id]) + assert.NotNil(t, postList.Posts[post6.Id]) + }) + + t.Run("should return all posts in a channel included deleted posts without threads", func(t *testing.T) { + err := ss.Post().Delete(post5.Id, 1, userId) + require.NoError(t, err) + + postList, err := ss.Post().GetPosts(model.GetPostsOptions{ChannelId: channelId, Page: 0, PerPage: 30, SkipFetchThreads: true, IncludeDeleted: true}, false, map[string]bool{}) + require.NoError(t, err) + + assert.Equal(t, []string{ + post6.Id, + post5.Id, + post4.Id, + post3.Id, + post2.Id, + post1.Id, + }, postList.Order) + + assert.Len(t, postList.Posts, 6) + assert.NotNil(t, postList.Posts[post5.Id]) + assert.NotNil(t, postList.Posts[post6.Id]) + assert.Equal(t, int64(1), postList.Posts[post5.Id].ReplyCount) + assert.Equal(t, int64(1), postList.Posts[post6.Id].ReplyCount) + }) + + t.Run("should return the lasts posts created in channel without include deleted posts", func(t *testing.T) { + err := ss.Post().Delete(post6.Id, 1, userId) + require.NoError(t, err) + + postList, err := ss.Post().GetPosts(model.GetPostsOptions{ChannelId: channelId, Page: 0, PerPage: 30, SkipFetchThreads: true, IncludeDeleted: false}, false, map[string]bool{}) + require.NoError(t, err) + + assert.Equal(t, []string{ + post4.Id, + post3.Id, + post2.Id, + }, postList.Order) + + assert.Len(t, postList.Posts, 3) + assert.NotNil(t, postList.Posts[post2.Id]) + assert.NotNil(t, postList.Posts[post3.Id]) + assert.NotNil(t, postList.Posts[post4.Id]) + }) } func testPostStoreGetPostBeforeAfter(t *testing.T, ss store.Store) {