Adding ability to fetch posts around a specifc post
Этот коммит содержится в:
66
api/post.go
66
api/post.go
@@ -31,6 +31,8 @@ func InitPost(r *mux.Router) {
|
||||
sr.Handle("/posts/{time:[0-9]+}", ApiUserRequiredActivity(getPostsSince, false)).Methods("GET")
|
||||
sr.Handle("/post/{post_id:[A-Za-z0-9]+}", ApiUserRequired(getPost)).Methods("GET")
|
||||
sr.Handle("/post/{post_id:[A-Za-z0-9]+}/delete", ApiUserRequired(deletePost)).Methods("POST")
|
||||
sr.Handle("/post/{post_id:[A-Za-z0-9]+}/before/{offset:[0-9]+}/{num_posts:[0-9]+}", ApiUserRequired(getPostsBefore)).Methods("GET")
|
||||
sr.Handle("/post/{post_id:[A-Za-z0-9]+}/after/{offset:[0-9]+}/{num_posts:[0-9]+}", ApiUserRequired(getPostsAfter)).Methods("GET")
|
||||
}
|
||||
|
||||
func createPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
@@ -812,6 +814,70 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func getPostsBefore(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
getPostsBeforeOrAfter(c, w, r, true)
|
||||
}
|
||||
func getPostsAfter(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
getPostsBeforeOrAfter(c, w, r, false)
|
||||
}
|
||||
func getPostsBeforeOrAfter(c *Context, w http.ResponseWriter, r *http.Request, before bool) {
|
||||
params := mux.Vars(r)
|
||||
|
||||
id := params["id"]
|
||||
if len(id) != 26 {
|
||||
c.SetInvalidParam("getPostsBeforeOrAfter", "channelId")
|
||||
return
|
||||
}
|
||||
|
||||
postId := params["post_id"]
|
||||
if len(postId) != 26 {
|
||||
c.SetInvalidParam("getPostsBeforeOrAfter", "postId")
|
||||
return
|
||||
}
|
||||
|
||||
numPosts, err := strconv.Atoi(params["num_posts"])
|
||||
if err != nil || numPosts <= 0 {
|
||||
c.SetInvalidParam("getPostsBeforeOrAfter", "numPosts")
|
||||
return
|
||||
}
|
||||
|
||||
offset, err := strconv.Atoi(params["offset"])
|
||||
if err != nil || offset < 0 {
|
||||
c.SetInvalidParam("getPostsBeforeOrAfter", "offset")
|
||||
return
|
||||
}
|
||||
|
||||
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, id, c.Session.UserId)
|
||||
// We can do better than this etag in this situation
|
||||
etagChan := Srv.Store.Post().GetEtag(id)
|
||||
|
||||
if !c.HasPermissionsToChannel(cchan, "getPostsBeforeOrAfter") {
|
||||
return
|
||||
}
|
||||
|
||||
etag := (<-etagChan).Data.(string)
|
||||
if HandleEtag(etag, w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
var pchan store.StoreChannel
|
||||
if before {
|
||||
pchan = Srv.Store.Post().GetPostsBefore(id, postId, numPosts, offset)
|
||||
} else {
|
||||
pchan = Srv.Store.Post().GetPostsAfter(id, postId, numPosts, offset)
|
||||
}
|
||||
|
||||
if result := <-pchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
list := result.Data.(*model.PostList)
|
||||
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
|
||||
w.Write([]byte(list.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
terms := r.FormValue("terms")
|
||||
|
||||
|
||||
@@ -329,6 +329,83 @@ func TestGetPostsSince(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPostsBeforeAfter(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post0 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
post0 = Client.Must(Client.CreatePost(post0)).Data.(*model.Post)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post1a1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: post1.Id}
|
||||
post1a1 = Client.Must(Client.CreatePost(post1a1)).Data.(*model.Post)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post3 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
|
||||
post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
post3a1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: post3.Id}
|
||||
post3a1 = Client.Must(Client.CreatePost(post3a1)).Data.(*model.Post)
|
||||
|
||||
r1 := Client.Must(Client.GetPostsBefore(channel1.Id, post1a1.Id, 0, 10, "")).Data.(*model.PostList)
|
||||
|
||||
if r1.Order[0] != post1.Id {
|
||||
t.Fatal("wrong order")
|
||||
}
|
||||
|
||||
if r1.Order[1] != post0.Id {
|
||||
t.Fatal("wrong order")
|
||||
}
|
||||
|
||||
if len(r1.Posts) != 2 {
|
||||
t.Fatal("wrong size")
|
||||
}
|
||||
|
||||
r2 := Client.Must(Client.GetPostsAfter(channel1.Id, post3a1.Id, 0, 3, "")).Data.(*model.PostList)
|
||||
|
||||
if len(r2.Posts) != 0 {
|
||||
t.Fatal("should have been empty")
|
||||
}
|
||||
|
||||
post2.Message = "new message"
|
||||
Client.Must(Client.UpdatePost(post2))
|
||||
|
||||
r3 := Client.Must(Client.GetPostsAfter(channel1.Id, post1a1.Id, 0, 2, "")).Data.(*model.PostList)
|
||||
|
||||
if r3.Order[0] != post3.Id {
|
||||
t.Fatal("wrong order")
|
||||
}
|
||||
|
||||
if r3.Order[1] != post2.Id {
|
||||
t.Fatal("wrong order")
|
||||
}
|
||||
|
||||
if len(r3.Order) != 2 {
|
||||
t.Fatal("missing post update")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchPosts(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
|
||||
@@ -618,6 +618,24 @@ func (c *Client) GetPostsSince(channelId string, time int64) (*Result, *AppError
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetPostsBefore(channelId string, postid string, offset int, limit int, etag string) (*Result, *AppError) {
|
||||
if r, err := c.DoApiGet(fmt.Sprintf("/channels/%v/post/%v/before/%v/%v", channelId, postid, offset, limit), "", etag); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetPostsAfter(channelId string, postid string, offset int, limit int, etag string) (*Result, *AppError) {
|
||||
if r, err := c.DoApiGet(fmt.Sprintf("/channels/%v/post/%v/after/%v/%v", channelId, postid, offset, limit), "", etag); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetPost(channelId string, postId string, etag string) (*Result, *AppError) {
|
||||
if r, err := c.DoApiGet(fmt.Sprintf("/channels/%v/post/%v", channelId, postId), "", etag); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -332,6 +332,104 @@ func (s SqlPostStore) GetPostsSince(channelId string, time int64) StoreChannel {
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlPostStore) GetPostsBefore(channelId string, postId string, numPosts int, offset int) StoreChannel {
|
||||
return s.getPostsAround(channelId, postId, numPosts, offset, true)
|
||||
}
|
||||
|
||||
func (s SqlPostStore) GetPostsAfter(channelId string, postId string, numPosts int, offset int) StoreChannel {
|
||||
return s.getPostsAround(channelId, postId, numPosts, offset, false)
|
||||
}
|
||||
|
||||
func (s SqlPostStore) getPostsAround(channelId string, postId string, numPosts int, offset int, before bool) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var direction string
|
||||
var sort string
|
||||
if before {
|
||||
direction = "<"
|
||||
sort = "DESC"
|
||||
} else {
|
||||
direction = ">"
|
||||
sort = "ASC"
|
||||
}
|
||||
|
||||
var posts []*model.Post
|
||||
var parents []*model.Post
|
||||
_, err1 := s.GetReplica().Select(&posts,
|
||||
`(SELECT
|
||||
*
|
||||
FROM
|
||||
Posts
|
||||
WHERE
|
||||
(CreateAt `+direction+` (SELECT CreateAt FROM Posts WHERE Id = :PostId)
|
||||
AND ChannelId = :ChannelId
|
||||
AND DeleteAt = 0)
|
||||
ORDER BY CreateAt `+sort+`
|
||||
LIMIT :NumPosts
|
||||
OFFSET :Offset)`,
|
||||
map[string]interface{}{"ChannelId": channelId, "PostId": postId, "NumPosts": numPosts, "Offset": offset})
|
||||
_, err2 := s.GetReplica().Select(&parents,
|
||||
`(SELECT
|
||||
*
|
||||
FROM
|
||||
Posts
|
||||
WHERE
|
||||
Id
|
||||
IN
|
||||
(SELECT * FROM (SELECT
|
||||
RootId
|
||||
FROM
|
||||
Posts
|
||||
WHERE
|
||||
(CreateAt `+direction+` (SELECT CreateAt FROM Posts WHERE Id = :PostId)
|
||||
AND ChannelId = :ChannelId
|
||||
AND DeleteAt = 0)
|
||||
ORDER BY CreateAt `+sort+`
|
||||
LIMIT :NumPosts
|
||||
OFFSET :Offset)
|
||||
temp_tab))
|
||||
ORDER BY CreateAt DESC`,
|
||||
map[string]interface{}{"ChannelId": channelId, "PostId": postId, "NumPosts": numPosts, "Offset": offset})
|
||||
|
||||
if err1 != nil {
|
||||
result.Err = model.NewAppError("SqlPostStore.GetPostContext", "We couldn't get the posts for the channel", "channelId="+channelId+err1.Error())
|
||||
} else if err2 != nil {
|
||||
result.Err = model.NewAppError("SqlPostStore.GetPostContext", "We couldn't get the parent posts for the channel", "channelId="+channelId+err2.Error())
|
||||
} else {
|
||||
|
||||
list := &model.PostList{Order: make([]string, 0, len(posts))}
|
||||
|
||||
// We need to flip the order if we selected backwards
|
||||
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 {
|
||||
list.AddPost(p)
|
||||
}
|
||||
|
||||
result.Data = list
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlPostStore) getRootPosts(channelId string, offset int, limit int) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
|
||||
@@ -383,6 +383,111 @@ func TestPostStoreGetPostsWtihDetails(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostStoreGetPostsBeforeAfter(t *testing.T) {
|
||||
Setup()
|
||||
o0 := &model.Post{}
|
||||
o0.ChannelId = model.NewId()
|
||||
o0.UserId = model.NewId()
|
||||
o0.Message = "a" + model.NewId() + "b"
|
||||
o0 = (<-store.Post().Save(o0)).Data.(*model.Post)
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
|
||||
o1 := &model.Post{}
|
||||
o1.ChannelId = model.NewId()
|
||||
o1.UserId = model.NewId()
|
||||
o1.Message = "a" + model.NewId() + "b"
|
||||
o1 = (<-store.Post().Save(o1)).Data.(*model.Post)
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
|
||||
o2 := &model.Post{}
|
||||
o2.ChannelId = o1.ChannelId
|
||||
o2.UserId = model.NewId()
|
||||
o2.Message = "a" + model.NewId() + "b"
|
||||
o2.ParentId = o1.Id
|
||||
o2.RootId = o1.Id
|
||||
o2 = (<-store.Post().Save(o2)).Data.(*model.Post)
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
|
||||
o2a := &model.Post{}
|
||||
o2a.ChannelId = o1.ChannelId
|
||||
o2a.UserId = model.NewId()
|
||||
o2a.Message = "a" + model.NewId() + "b"
|
||||
o2a.ParentId = o1.Id
|
||||
o2a.RootId = o1.Id
|
||||
o2a = (<-store.Post().Save(o2a)).Data.(*model.Post)
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
|
||||
o3 := &model.Post{}
|
||||
o3.ChannelId = o1.ChannelId
|
||||
o3.UserId = model.NewId()
|
||||
o3.Message = "a" + model.NewId() + "b"
|
||||
o3.ParentId = o1.Id
|
||||
o3.RootId = o1.Id
|
||||
o3 = (<-store.Post().Save(o3)).Data.(*model.Post)
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
|
||||
o4 := &model.Post{}
|
||||
o4.ChannelId = o1.ChannelId
|
||||
o4.UserId = model.NewId()
|
||||
o4.Message = "a" + model.NewId() + "b"
|
||||
o4 = (<-store.Post().Save(o4)).Data.(*model.Post)
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
|
||||
o5 := &model.Post{}
|
||||
o5.ChannelId = o1.ChannelId
|
||||
o5.UserId = model.NewId()
|
||||
o5.Message = "a" + model.NewId() + "b"
|
||||
o5.ParentId = o4.Id
|
||||
o5.RootId = o4.Id
|
||||
o5 = (<-store.Post().Save(o5)).Data.(*model.Post)
|
||||
|
||||
r1 := (<-store.Post().GetPostsBefore(o1.ChannelId, o1.Id, 4, 0)).Data.(*model.PostList)
|
||||
|
||||
if len(r1.Posts) != 0 {
|
||||
t.Fatal("Wrong size")
|
||||
}
|
||||
|
||||
r2 := (<-store.Post().GetPostsAfter(o1.ChannelId, o1.Id, 4, 0)).Data.(*model.PostList)
|
||||
|
||||
if r2.Order[0] != o4.Id {
|
||||
t.Fatal("invalid order")
|
||||
}
|
||||
|
||||
if r2.Order[1] != o3.Id {
|
||||
t.Fatal("invalid order")
|
||||
}
|
||||
|
||||
if r2.Order[2] != o2a.Id {
|
||||
t.Fatal("invalid order")
|
||||
}
|
||||
|
||||
if r2.Order[3] != o2.Id {
|
||||
t.Fatal("invalid order")
|
||||
}
|
||||
|
||||
if len(r2.Posts) != 5 {
|
||||
t.Fatal("wrong size")
|
||||
}
|
||||
|
||||
r3 := (<-store.Post().GetPostsBefore(o3.ChannelId, o3.Id, 2, 0)).Data.(*model.PostList)
|
||||
|
||||
if r3.Order[0] != o2a.Id {
|
||||
t.Fatal("invalid order")
|
||||
}
|
||||
|
||||
if r3.Order[1] != o2.Id {
|
||||
t.Fatal("invalid order")
|
||||
}
|
||||
|
||||
if len(r3.Posts) != 3 {
|
||||
t.Fatal("wrong size")
|
||||
}
|
||||
|
||||
if r3.Posts[o1.Id].Message != o1.Message {
|
||||
t.Fatal("Missing parent")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostStoreGetPostsSince(t *testing.T) {
|
||||
Setup()
|
||||
o0 := &model.Post{}
|
||||
|
||||
@@ -86,6 +86,8 @@ type PostStore interface {
|
||||
Get(id string) StoreChannel
|
||||
Delete(postId string, time int64) StoreChannel
|
||||
GetPosts(channelId string, offset int, limit int) StoreChannel
|
||||
GetPostsBefore(channelId string, postId string, numPosts int, offset int) StoreChannel
|
||||
GetPostsAfter(channelId string, postId string, numPosts int, offset int) StoreChannel
|
||||
GetPostsSince(channelId string, time int64) StoreChannel
|
||||
GetEtag(channelId string) StoreChannel
|
||||
Search(teamId string, userId string, params *model.SearchParams) StoreChannel
|
||||
|
||||
Ссылка в новой задаче
Block a user