MM-11210 Add "GET /posts/unread" API to support landing on the last unread post (#11486)
* [MM-11210] Add API GET 'api/v4/channels/{channel_id:[A-Za-z0-9]+}/posts/unread' for scrolling overhaul (#9108)
* Add API GET 'api/v4/channels/{channel_id:[A-Za-z0-9]+}/posts/unread'
* add constants
* refactor GetPostSince and added more tests
* move constants to app package
* [MM-11528 && MM-11583] Add userId to in the "posts/unread" path and update test with time delay to fix intermittent failure (#9229)
* add userId to in the "posts/unread" path and update test with time delay to fix intermittent failure
* add limit before and after to query
* remove time delay on test and put pretermined value of Post.CreateAt
* Fix conflict
* [MM-11876] Add cursor to posts list such as next_post_id and previous_post_id (#9707)
* add cursor to posts list such as next_post_id and previous_post_id
add publish previous_post_id on WEBSOCKET_EVENT_POSTED and only get next or previous post IDs if necessary
revert change on adding previous_post_id in WEBSOCKET_EVENT_POSTED
add missing strings import
fix merge conflicts
* update per comment
* update per feedback
* corrected the logic in getting the next and previous post ID
* fix logic to determine next and post IDs, and rename function to have suffix of "Time"
* rearrange logics and add mote tests
* fix merge conflict
* fix missing message when using unread API (#10233)
* MM-15569 Fixes failing test on TestGetPostsForChannelAroundLastUnread (#11039)
* Fix missing posts when getting posts since
* revert changes to GetPostsSince
* migrate Post.GetPostAfterTime and Post.GetPostBeforeTime to sync by default
* revert change to cacheItem
* Fix post ID validation, build query on squirrel and only return post ID as necessary
Этот коммит содержится в:
коммит произвёл
Sudheer
родитель
f56a8f5a99
Коммит
b832985f1d
88
api4/post.go
88
api4/post.go
@@ -9,6 +9,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
@@ -22,6 +23,8 @@ func (api *API) InitPost() {
|
||||
api.BaseRoutes.PostsForChannel.Handle("", api.ApiSessionRequired(getPostsForChannel)).Methods("GET")
|
||||
api.BaseRoutes.PostsForUser.Handle("/flagged", api.ApiSessionRequired(getFlaggedPostsForUser)).Methods("GET")
|
||||
|
||||
api.BaseRoutes.ChannelForUser.Handle("/posts/unread", api.ApiSessionRequired(getPostsForChannelAroundLastUnread)).Methods("GET")
|
||||
|
||||
api.BaseRoutes.Team.Handle("/posts/search", api.ApiSessionRequired(searchPosts)).Methods("POST")
|
||||
api.BaseRoutes.Post.Handle("", api.ApiSessionRequired(updatePost)).Methods("PUT")
|
||||
api.BaseRoutes.Post.Handle("/patch", api.ApiSessionRequired(patchPost)).Methods("PUT")
|
||||
@@ -109,12 +112,20 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
afterPost := r.URL.Query().Get("after")
|
||||
beforePost := r.URL.Query().Get("before")
|
||||
sinceString := r.URL.Query().Get("since")
|
||||
if len(afterPost) > 0 && !model.IsValidId(afterPost) {
|
||||
c.SetInvalidParam("after")
|
||||
return
|
||||
}
|
||||
|
||||
beforePost := r.URL.Query().Get("before")
|
||||
if len(beforePost) > 0 && !model.IsValidId(beforePost) {
|
||||
c.SetInvalidParam("before")
|
||||
return
|
||||
}
|
||||
|
||||
sinceString := r.URL.Query().Get("since")
|
||||
var since int64
|
||||
var parseError error
|
||||
|
||||
if len(sinceString) > 0 {
|
||||
since, parseError = strconv.ParseInt(sinceString, 10, 64)
|
||||
if parseError != nil {
|
||||
@@ -123,7 +134,11 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.App.Session, c.Params.ChannelId, model.PERMISSION_READ_CHANNEL) {
|
||||
channelId := c.Params.ChannelId
|
||||
page := c.Params.Page
|
||||
perPage := c.Params.PerPage
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.App.Session, channelId, model.PERMISSION_READ_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return
|
||||
}
|
||||
@@ -133,31 +148,31 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
etag := ""
|
||||
|
||||
if since > 0 {
|
||||
list, err = c.App.GetPostsSince(c.Params.ChannelId, since)
|
||||
list, err = c.App.GetPostsSince(channelId, since)
|
||||
} else if len(afterPost) > 0 {
|
||||
etag = c.App.GetPostsEtag(c.Params.ChannelId)
|
||||
etag = c.App.GetPostsEtag(channelId)
|
||||
|
||||
if c.HandleEtag(etag, "Get Posts After", w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
list, err = c.App.GetPostsAfterPost(c.Params.ChannelId, afterPost, c.Params.Page, c.Params.PerPage)
|
||||
list, err = c.App.GetPostsAfterPost(channelId, afterPost, page, perPage)
|
||||
} else if len(beforePost) > 0 {
|
||||
etag = c.App.GetPostsEtag(c.Params.ChannelId)
|
||||
etag = c.App.GetPostsEtag(channelId)
|
||||
|
||||
if c.HandleEtag(etag, "Get Posts Before", w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
list, err = c.App.GetPostsBeforePost(c.Params.ChannelId, beforePost, c.Params.Page, c.Params.PerPage)
|
||||
list, err = c.App.GetPostsBeforePost(channelId, beforePost, page, perPage)
|
||||
} else {
|
||||
etag = c.App.GetPostsEtag(c.Params.ChannelId)
|
||||
etag = c.App.GetPostsEtag(channelId)
|
||||
|
||||
if c.HandleEtag(etag, "Get Posts", w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
list, err = c.App.GetPostsPage(c.Params.ChannelId, c.Params.Page, c.Params.PerPage)
|
||||
list, err = c.App.GetPostsPage(channelId, page, perPage)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -169,7 +184,56 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
|
||||
}
|
||||
|
||||
w.Write([]byte(c.App.PreparePostListForClient(list).ToJson()))
|
||||
c.App.AddCursorIdsForPostList(list, afterPost, beforePost, since, page, perPage)
|
||||
clientPostList := c.App.PreparePostListForClient(list)
|
||||
|
||||
w.Write([]byte(clientPostList.ToJson()))
|
||||
}
|
||||
|
||||
func getPostsForChannelAroundLastUnread(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireUserId().RequireChannelId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
userId := c.Params.UserId
|
||||
if !c.App.SessionHasPermissionToUser(c.App.Session, userId) {
|
||||
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
|
||||
return
|
||||
}
|
||||
|
||||
channelId := c.Params.ChannelId
|
||||
if !c.App.SessionHasPermissionToChannel(c.App.Session, channelId, model.PERMISSION_READ_CHANNEL) {
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
postList, err := c.App.GetPostsForChannelAroundLastUnread(channelId, userId, c.Params.LimitBefore, c.Params.LimitAfter)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
etag := ""
|
||||
if len(postList.Order) == 0 {
|
||||
etag = c.App.GetPostsEtag(channelId)
|
||||
|
||||
if c.HandleEtag(etag, "Get Posts", w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
postList, err = c.App.GetPostsPage(channelId, app.PAGE_DEFAULT, c.Params.LimitBefore)
|
||||
}
|
||||
|
||||
postList.NextPostId = c.App.GetNextPostIdFromPostList(postList)
|
||||
postList.PrevPostId = c.App.GetPrevPostIdFromPostList(postList)
|
||||
|
||||
clientPostList := c.App.PreparePostListForClient(postList)
|
||||
|
||||
if len(etag) > 0 {
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
|
||||
}
|
||||
w.Write([]byte(clientPostList.ToJson()))
|
||||
}
|
||||
|
||||
func getFlaggedPostsForUser(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
@@ -911,6 +912,13 @@ func TestGetPostsForChannel(t *testing.T) {
|
||||
t.Log(posts.Posts)
|
||||
t.Fatal("should return 2 posts")
|
||||
}
|
||||
// "since" query to return empty NextPostId and PrevPostId
|
||||
if posts.NextPostId != "" {
|
||||
t.Fatal("should return an empty NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != "" {
|
||||
t.Fatal("should return an empty PrevPostId")
|
||||
}
|
||||
|
||||
found := make([]bool, 2)
|
||||
for _, p := range posts.Posts {
|
||||
@@ -945,6 +953,97 @@ func TestGetPostsForChannel(t *testing.T) {
|
||||
|
||||
_, resp = th.SystemAdminClient.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
// more tests for next_post_id, prev_post_id, and order
|
||||
// There are 12 posts composed of first 2 system messages and 10 created posts
|
||||
Client.Login(th.BasicUser.Email, th.BasicUser.Password)
|
||||
th.CreatePost() // post6
|
||||
post7 := th.CreatePost()
|
||||
post8 := th.CreatePost()
|
||||
th.CreatePost() // post9
|
||||
post10 := th.CreatePost()
|
||||
|
||||
// get the system post IDs posted before the created posts above
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "")
|
||||
systemPostId1 := posts.Order[1]
|
||||
|
||||
// similar to '/posts'
|
||||
posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 12 || posts.Order[0] != post10.Id || posts.Order[11] != systemPostId1 {
|
||||
t.Fatal("should return 12 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != "" {
|
||||
t.Fatal("should return an empty NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != "" {
|
||||
t.Fatal("should return an empty PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?per_page=3'
|
||||
posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 0, 3, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 3 || posts.Order[0] != post10.Id || posts.Order[2] != post8.Id {
|
||||
t.Fatal("should return 3 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != "" {
|
||||
t.Fatal("should return an empty NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post7.Id {
|
||||
t.Fatal("should return post7.Id as PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?per_page=3&page=1'
|
||||
posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 1, 3, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 3 || posts.Order[0] != post7.Id || posts.Order[2] != post5.Id {
|
||||
t.Fatal("should return 3 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != post8.Id {
|
||||
t.Fatal("should return post8.Id as NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post4.Id {
|
||||
t.Fatal("should return post4.Id as PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?per_page=3&page=2'
|
||||
posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 2, 3, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 3 || posts.Order[0] != post4.Id || posts.Order[2] != post2.Id {
|
||||
t.Fatal("should return 3 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != post5.Id {
|
||||
t.Fatal("should return post5.Id as NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post1.Id {
|
||||
t.Fatal("should return post1.Id as PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?per_page=3&page=3'
|
||||
posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 3, 3, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 3 || posts.Order[0] != post1.Id || posts.Order[2] != systemPostId1 {
|
||||
t.Fatal("should return 3 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != post2.Id {
|
||||
t.Fatal("should return post2.Id as NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != "" {
|
||||
t.Fatal("should return an empty PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?per_page=3&page=4'
|
||||
posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 4, 3, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 0 {
|
||||
t.Fatal("should return 0 post")
|
||||
}
|
||||
if posts.NextPostId != "" {
|
||||
t.Fatal("should return an empty NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != "" {
|
||||
t.Fatal("should return an empty PrevPostId")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFlaggedPostsForUser(t *testing.T) {
|
||||
@@ -1190,7 +1289,7 @@ func TestGetFlaggedPostsForUser(t *testing.T) {
|
||||
CheckNoError(t, resp)
|
||||
}
|
||||
|
||||
func TestGetPostsAfterAndBefore(t *testing.T) {
|
||||
func TestGetPostsBefore(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
Client := th.Client
|
||||
@@ -1223,24 +1322,208 @@ func TestGetPostsAfterAndBefore(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post3.Id, 1, 1, "")
|
||||
if posts.NextPostId != post3.Id {
|
||||
t.Fatal("should match NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != "" {
|
||||
t.Fatal("should match empty PrevPostId")
|
||||
}
|
||||
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post4.Id, 1, 1, "")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
if len(posts.Posts) != 1 {
|
||||
t.Fatal("too many posts returned")
|
||||
}
|
||||
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, "junk", 1, 1, "")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
if len(posts.Posts) != 0 {
|
||||
t.Fatal("should have no posts")
|
||||
if posts.Order[0] != post2.Id {
|
||||
t.Fatal("should match returned post")
|
||||
}
|
||||
if posts.NextPostId != post3.Id {
|
||||
t.Fatal("should match NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post1.Id {
|
||||
t.Fatal("should match PrevPostId")
|
||||
}
|
||||
|
||||
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post3.Id, 0, 100, "")
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, "junk", 1, 1, "")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post5.Id, 0, 3, "")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
found = make([]bool, 2)
|
||||
if len(posts.Posts) != 3 {
|
||||
t.Fatal("should match length of posts returned")
|
||||
}
|
||||
if posts.Order[0] != post4.Id {
|
||||
t.Fatal("should match returned post")
|
||||
}
|
||||
if posts.Order[2] != post2.Id {
|
||||
t.Fatal("should match returned post")
|
||||
}
|
||||
if posts.NextPostId != post5.Id {
|
||||
t.Fatal("should match NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post1.Id {
|
||||
t.Fatal("should match PrevPostId")
|
||||
}
|
||||
|
||||
// get the system post IDs posted before the created posts above
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "")
|
||||
CheckNoError(t, resp)
|
||||
systemPostId2 := posts.Order[0]
|
||||
systemPostId1 := posts.Order[1]
|
||||
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post5.Id, 1, 3, "")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
if len(posts.Posts) != 3 {
|
||||
t.Fatal("should match length of posts returned")
|
||||
}
|
||||
if posts.Order[0] != post1.Id {
|
||||
t.Fatal("should match returned post")
|
||||
}
|
||||
if posts.Order[1] != systemPostId2 {
|
||||
t.Fatal("should match returned post")
|
||||
}
|
||||
if posts.Order[2] != systemPostId1 {
|
||||
t.Fatal("should match returned post")
|
||||
}
|
||||
if posts.NextPostId != post2.Id {
|
||||
t.Fatal("should match NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != "" {
|
||||
t.Fatal("should return empty PrevPostId")
|
||||
}
|
||||
|
||||
// more tests for next_post_id, prev_post_id, and order
|
||||
// There are 12 posts composed of first 2 system messages and 10 created posts
|
||||
post6 := th.CreatePost()
|
||||
th.CreatePost() // post7
|
||||
post8 := th.CreatePost()
|
||||
post9 := th.CreatePost()
|
||||
th.CreatePost() // post10
|
||||
|
||||
// similar to '/posts?before=post9'
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 0, 60, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 10 || posts.Order[0] != post8.Id || posts.Order[9] != systemPostId1 {
|
||||
t.Fatal("should return 10 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != post9.Id {
|
||||
t.Fatal("should return post9.Id as NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != "" {
|
||||
t.Fatal("should return an empty PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?before=post9&per_page=3'
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 0, 3, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 3 || posts.Order[0] != post8.Id || posts.Order[2] != post6.Id {
|
||||
t.Fatal("should return 3 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != post9.Id {
|
||||
t.Fatal("should return post9.Id as NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post5.Id {
|
||||
t.Fatal("should return post5.Id as PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?before=post9&per_page=3&page=1'
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 1, 3, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 3 || posts.Order[0] != post5.Id || posts.Order[2] != post3.Id {
|
||||
t.Fatal("should return 3 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != post6.Id {
|
||||
t.Fatal("should return post6.Id as NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post2.Id {
|
||||
t.Fatal("should return post2.Id as PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?before=post9&per_page=3&page=2'
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 2, 3, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 3 || posts.Order[0] != post2.Id || posts.Order[2] != systemPostId2 {
|
||||
t.Fatal("should return 3 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != post3.Id {
|
||||
t.Fatal("should return post3.Id as NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != systemPostId1 {
|
||||
t.Fatal("should return systemPostId1 as PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?before=post1&per_page=3'
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 3, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 2 || posts.Order[0] != systemPostId2 || posts.Order[1] != systemPostId1 {
|
||||
t.Fatal("should return 2 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != post1.Id {
|
||||
t.Fatal("should return post1.Id as NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != "" {
|
||||
t.Fatal("should return an empty PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?before=systemPostId1'
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, systemPostId1, 0, 60, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 0 {
|
||||
t.Fatal("should return 0 post")
|
||||
}
|
||||
if posts.NextPostId != systemPostId1 {
|
||||
t.Fatal("should return systemPostId1 as NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != "" {
|
||||
t.Fatal("should return an empty PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?before=systemPostId1&per_page=60&page=1'
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, systemPostId1, 1, 60, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 0 {
|
||||
t.Fatal("should return 0 post")
|
||||
}
|
||||
if posts.NextPostId != "" {
|
||||
t.Fatal("should return an empty NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != "" {
|
||||
t.Fatal("should return an empty PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?before=non-existent-post'
|
||||
nonExistentPostId := model.NewId()
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, nonExistentPostId, 0, 60, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 0 {
|
||||
t.Fatal("should return 0 post")
|
||||
}
|
||||
if posts.NextPostId != nonExistentPostId {
|
||||
t.Fatal("should return nonExistentPostId as NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != "" {
|
||||
t.Fatal("should return an empty PrevPostId")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPostsAfter(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
Client := th.Client
|
||||
|
||||
post1 := th.CreatePost()
|
||||
post2 := th.CreatePost()
|
||||
post3 := th.CreatePost()
|
||||
post4 := th.CreatePost()
|
||||
post5 := th.CreatePost()
|
||||
|
||||
posts, resp := Client.GetPostsAfter(th.BasicChannel.Id, post3.Id, 0, 100, "")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
found := make([]bool, 2)
|
||||
for _, p := range posts.Posts {
|
||||
if p.Id == post4.Id {
|
||||
found[0] = true
|
||||
@@ -1259,18 +1542,298 @@ func TestGetPostsAfterAndBefore(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post3.Id, 1, 1, "")
|
||||
if posts.NextPostId != "" {
|
||||
t.Fatal("should match empty NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post3.Id {
|
||||
t.Fatal("should match PrevPostId")
|
||||
}
|
||||
|
||||
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 1, 1, "")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
if len(posts.Posts) != 1 {
|
||||
t.Fatal("too many posts returned")
|
||||
}
|
||||
if posts.Order[0] != post4.Id {
|
||||
t.Fatal("should match returned post")
|
||||
}
|
||||
if posts.NextPostId != post5.Id {
|
||||
t.Fatal("should match NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post3.Id {
|
||||
t.Fatal("should match PrevPostId")
|
||||
}
|
||||
|
||||
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, "junk", 1, 1, "")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post1.Id, 0, 3, "")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
if len(posts.Posts) != 0 {
|
||||
t.Fatal("should have no posts")
|
||||
if len(posts.Posts) != 3 {
|
||||
t.Fatal("should match length of posts returned")
|
||||
}
|
||||
if posts.Order[0] != post4.Id {
|
||||
t.Fatal("should match returned post")
|
||||
}
|
||||
if posts.Order[2] != post2.Id {
|
||||
t.Fatal("should match returned post")
|
||||
}
|
||||
if posts.NextPostId != post5.Id {
|
||||
t.Fatal("should match NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post1.Id {
|
||||
t.Fatal("should match PrevPostId")
|
||||
}
|
||||
|
||||
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post1.Id, 1, 3, "")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
if len(posts.Posts) != 1 {
|
||||
t.Fatal("should match length of posts returned")
|
||||
}
|
||||
if posts.Order[0] != post5.Id {
|
||||
t.Fatal("should match returned post")
|
||||
}
|
||||
if posts.NextPostId != "" {
|
||||
t.Fatal("should match NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post4.Id {
|
||||
t.Fatal("should match PrevPostId")
|
||||
}
|
||||
|
||||
// more tests for next_post_id, prev_post_id, and order
|
||||
// There are 12 posts composed of first 2 system messages and 10 created posts
|
||||
post6 := th.CreatePost()
|
||||
th.CreatePost() // post7
|
||||
post8 := th.CreatePost()
|
||||
post9 := th.CreatePost()
|
||||
post10 := th.CreatePost()
|
||||
|
||||
// similar to '/posts?after=post2'
|
||||
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 0, 60, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 8 || posts.Order[0] != post10.Id || posts.Order[7] != post3.Id {
|
||||
t.Fatal("should return 8 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != "" {
|
||||
t.Fatal("should return an empty NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post2.Id {
|
||||
t.Fatal("should return post2.Id as PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?after=post2&per_page=3'
|
||||
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 0, 3, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 3 || posts.Order[0] != post5.Id || posts.Order[2] != post3.Id {
|
||||
t.Fatal("should return 3 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != post6.Id {
|
||||
t.Fatal("should return post6.Id as NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post2.Id {
|
||||
t.Fatal("should return post2.Id as PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?after=post2&per_page=3&page=1'
|
||||
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 1, 3, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 3 || posts.Order[0] != post8.Id || posts.Order[2] != post6.Id {
|
||||
t.Fatal("should return 3 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != post9.Id {
|
||||
t.Fatal("should return post9.Id as NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post5.Id {
|
||||
t.Fatal("should return post5.Id as PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?after=post2&per_page=3&page=2'
|
||||
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 2, 3, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 2 || posts.Order[0] != post10.Id || posts.Order[1] != post9.Id {
|
||||
t.Fatal("should return 2 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != "" {
|
||||
t.Fatal("should return an empty NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post8.Id {
|
||||
t.Fatal("should return post8.Id as PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?after=post10'
|
||||
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post10.Id, 0, 60, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 0 {
|
||||
t.Fatal("should return 0 post")
|
||||
}
|
||||
if posts.NextPostId != "" {
|
||||
t.Fatal("should return an empty NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post10.Id {
|
||||
t.Fatal("should return post10.Id as PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?after=post10&page=1'
|
||||
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post10.Id, 1, 60, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 0 {
|
||||
t.Fatal("should return 0 post")
|
||||
}
|
||||
if posts.NextPostId != "" {
|
||||
t.Fatal("should return an empty NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != "" {
|
||||
t.Fatal("should return an empty PrevPostId")
|
||||
}
|
||||
|
||||
// similar to '/posts?after=non-existent-post'
|
||||
nonExistentPostId := model.NewId()
|
||||
posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, nonExistentPostId, 0, 60, "")
|
||||
CheckNoError(t, resp)
|
||||
if len(posts.Order) != 0 {
|
||||
t.Fatal("should return 0 post")
|
||||
}
|
||||
if posts.NextPostId != "" {
|
||||
t.Fatal("should return an empty NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != nonExistentPostId {
|
||||
t.Fatal("should return nonExistentPostId as PrevPostId")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPostsForChannelAroundLastUnread(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
Client := th.Client
|
||||
userId := th.BasicUser.Id
|
||||
channelId := th.BasicChannel.Id
|
||||
|
||||
// 12 posts = 2 systems posts + 10 created posts below
|
||||
post1 := th.CreatePost()
|
||||
post2 := th.CreatePost()
|
||||
post3 := th.CreatePost()
|
||||
post4 := th.CreatePost()
|
||||
th.CreatePost() // post5
|
||||
post6 := th.CreatePost()
|
||||
post7 := th.CreatePost()
|
||||
post8 := th.CreatePost()
|
||||
post9 := th.CreatePost()
|
||||
post10 := th.CreatePost()
|
||||
|
||||
// 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)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
if len(posts.Order) != 12 {
|
||||
t.Fatal("Should return 12 posts only since there's no unread post")
|
||||
}
|
||||
|
||||
// Set channel member's last viewed to 0.
|
||||
// All returned posts are latest posts as if all previous posts were already read by the user.
|
||||
channelMember, err := th.App.Srv.Store.Channel().GetMember(channelId, userId)
|
||||
require.Nil(t, err)
|
||||
channelMember.LastViewedAt = 0
|
||||
_, err = th.App.Srv.Store.Channel().UpdateMember(channelMember)
|
||||
require.Nil(t, err)
|
||||
th.App.Srv.Store.Post().InvalidateLastPostTimeCache(channelId)
|
||||
|
||||
posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 20, 20)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
if len(posts.Order) != 12 {
|
||||
t.Fatal("Should return 12 posts only since there's no unread post")
|
||||
}
|
||||
|
||||
// get the first system post generated before the created posts above
|
||||
posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "")
|
||||
CheckNoError(t, resp)
|
||||
systemPostId1 := posts.Order[1]
|
||||
|
||||
// Set channel member's last viewed before post1.
|
||||
channelMember, err = th.App.Srv.Store.Channel().GetMember(channelId, userId)
|
||||
require.Nil(t, err)
|
||||
channelMember.LastViewedAt = post1.CreateAt - 1
|
||||
_, err = th.App.Srv.Store.Channel().UpdateMember(channelMember)
|
||||
require.Nil(t, err)
|
||||
th.App.Srv.Store.Post().InvalidateLastPostTimeCache(channelId)
|
||||
|
||||
posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 3, 3)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
if len(posts.Order) != 5 || posts.Order[0] != post3.Id || posts.Order[4] != systemPostId1 {
|
||||
t.Fatal("Should return 5 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != post4.Id {
|
||||
t.Fatal("should return post4.Id as NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != "" {
|
||||
t.Fatal("should return an empty PrevPostId")
|
||||
}
|
||||
|
||||
// Set channel member's last viewed before post6.
|
||||
channelMember, err = th.App.Srv.Store.Channel().GetMember(channelId, userId)
|
||||
require.Nil(t, err)
|
||||
channelMember.LastViewedAt = post6.CreateAt - 1
|
||||
_, err = th.App.Srv.Store.Channel().UpdateMember(channelMember)
|
||||
require.Nil(t, err)
|
||||
th.App.Srv.Store.Post().InvalidateLastPostTimeCache(channelId)
|
||||
|
||||
posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 3, 3)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
if len(posts.Order) != 6 || posts.Order[0] != post8.Id || posts.Order[5] != post3.Id {
|
||||
t.Fatal("Should return 6 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != post9.Id {
|
||||
t.Fatal("should return post8.Id as NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post2.Id {
|
||||
t.Fatal("should return post2.Id as PrevPostId")
|
||||
}
|
||||
|
||||
// Set channel member's last viewed before post10.
|
||||
channelMember, err = th.App.Srv.Store.Channel().GetMember(channelId, userId)
|
||||
require.Nil(t, err)
|
||||
channelMember.LastViewedAt = post10.CreateAt - 1
|
||||
_, err = th.App.Srv.Store.Channel().UpdateMember(channelMember)
|
||||
require.Nil(t, err)
|
||||
th.App.Srv.Store.Post().InvalidateLastPostTimeCache(channelId)
|
||||
|
||||
posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 3, 3)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
if len(posts.Order) != 4 || posts.Order[0] != post10.Id || posts.Order[3] != post7.Id {
|
||||
t.Fatal("Should return 4 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != "" {
|
||||
t.Fatal("should return an empty NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post6.Id {
|
||||
t.Fatal("should return post6.Id as PrevPostId")
|
||||
}
|
||||
|
||||
// Set channel member's last viewed equal to post10.
|
||||
channelMember, err = th.App.Srv.Store.Channel().GetMember(channelId, userId)
|
||||
require.Nil(t, err)
|
||||
channelMember.LastViewedAt = post10.CreateAt
|
||||
_, err = th.App.Srv.Store.Channel().UpdateMember(channelMember)
|
||||
require.Nil(t, err)
|
||||
th.App.Srv.Store.Post().InvalidateLastPostTimeCache(channelId)
|
||||
|
||||
posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 3, 3)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
if len(posts.Order) != 3 || posts.Order[0] != post10.Id || posts.Order[2] != post8.Id {
|
||||
t.Fatal("Should return 3 posts and match order")
|
||||
}
|
||||
if posts.NextPostId != "" {
|
||||
t.Fatal("should return an empty NextPostId")
|
||||
}
|
||||
if posts.PrevPostId != post7.Id {
|
||||
t.Fatal("should return post7.Id as PrevPostId")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ссылка в новой задаче
Block a user