Skip to content

Commit

Permalink
[GH-20216] Query messages for a channel posted from a certain time (m…
Browse files Browse the repository at this point in the history
…attermost#521)

* Add --since flag to post list command

* Add unit tests for postListCmd and e2e tests for postListCmd & createPostCmd and some improvements

* Optimize e2e tests and some imporovements

Co-authored-by: Mattermod <[email protected]>
  • Loading branch information
nadeem-hassan and mattermod authored Aug 3, 2022
1 parent 3061fbf commit bf36650
Show file tree
Hide file tree
Showing 6 changed files with 369 additions and 11 deletions.
1 change: 1 addition & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type Client interface {
GetPost(postID string, etag string) (*model.Post, *model.Response, error)
CreatePost(post *model.Post) (*model.Post, *model.Response, error)
GetPostsForChannel(channelID string, page, perPage int, etag string, collapsedThreads bool) (*model.PostList, *model.Response, error)
GetPostsSince(channelID string, since int64, collapsedThreads bool) (*model.PostList, *model.Response, error)
DoAPIPost(url string, data string) (*http.Response, error)
GetLdapGroups() ([]*model.Group, *model.Response, error)
GetGroupsByChannel(channelID string, groupOpts model.GroupSearchOpts) ([]*model.GroupWithSchemeAdmin, int, *model.Response, error)
Expand Down
44 changes: 37 additions & 7 deletions commands/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package commands
import (
"encoding/json"
"fmt"
"time"

"github.com/mattermost/mattermost-server/v6/model"

Expand Down Expand Up @@ -38,13 +39,19 @@ var PostListCmd = &cobra.Command{
RunE: withClient(postListCmdF),
}

const (
ISO8601Layout = "2006-01-02T15:04:05-07:00"
PostTimeFormat = "2006-01-02 15:04:05-07:00"
)

func init() {
PostCreateCmd.Flags().StringP("message", "m", "", "Message for the post")
PostCreateCmd.Flags().StringP("reply-to", "r", "", "Post id to reply to")

PostListCmd.Flags().IntP("number", "n", 20, "Number of messages to list")
PostListCmd.Flags().BoolP("show-ids", "i", false, "Show posts ids")
PostListCmd.Flags().BoolP("follow", "f", false, "Output appended data as new messages are posted to the channel")
PostListCmd.Flags().StringP("since", "s", "", "List messages posted after a certain time (ISO 8601)")

PostCmd.AddCommand(
PostCreateCmd,
Expand Down Expand Up @@ -110,7 +117,7 @@ func eventDataToPost(eventData map[string]interface{}) (*model.Post, error) {
return post, nil
}

func printPost(c client.Client, post *model.Post, usernames map[string]string, showIds bool) {
func printPost(c client.Client, post *model.Post, usernames map[string]string, showIds, showTimestamp bool) {
var username string

if usernames[post.UserId] != "" {
Expand All @@ -125,11 +132,32 @@ func printPost(c client.Client, post *model.Post, usernames map[string]string, s
}
}

if showIds {
printer.PrintT(fmt.Sprintf("\u001b[31m%s\u001b[0m \u001b[34;1m[%s]\u001b[0m {{.Message}}", post.Id, username), post)
postTime := model.GetTimeForMillis(post.CreateAt)
createdAt := postTime.Format(PostTimeFormat)

if showTimestamp {
printer.PrintT(fmt.Sprintf("\u001b[32m%s\u001b[0m \u001b[34;1m[%s]\u001b[0m {{.Message}}", createdAt, username), post)
} else {
printer.PrintT(fmt.Sprintf("\u001b[34;1m[%s]\u001b[0m {{.Message}}", username), post)
if showIds {
printer.PrintT(fmt.Sprintf("\u001b[31m%s\u001b[0m \u001b[34;1m[%s]\u001b[0m {{.Message}}", post.Id, username), post)
} else {
printer.PrintT(fmt.Sprintf("\u001b[34;1m[%s]\u001b[0m {{.Message}}", username), post)
}
}
}

func getPostList(client client.Client, channelID, since string, perPage int) (*model.PostList, *model.Response, error) {
if since == "" {
return client.GetPostsForChannel(channelID, 0, perPage, "", false)
}

sinceTime, err := time.Parse(ISO8601Layout, since)
if err != nil {
return nil, nil, fmt.Errorf("invalid since time '%s'", since)
}

sinceTimeMillis := model.GetMillisForTime(sinceTime)
return client.GetPostsSince(channelID, sinceTimeMillis, false)
}

func postListCmdF(c client.Client, cmd *cobra.Command, args []string) error {
Expand All @@ -143,17 +171,19 @@ func postListCmdF(c client.Client, cmd *cobra.Command, args []string) error {
number, _ := cmd.Flags().GetInt("number")
showIds, _ := cmd.Flags().GetBool("show-ids")
follow, _ := cmd.Flags().GetBool("follow")
since, _ := cmd.Flags().GetString("since")

postList, _, err := c.GetPostsForChannel(channel.Id, 0, number, "", false)
postList, _, err := getPostList(c, channel.Id, since, number)
if err != nil {
return err
}

posts := postList.ToSlice()
showTimestamp := len(since) > 0
usernames := map[string]string{}
for i := 1; i <= len(posts); i++ {
post := posts[len(posts)-i]
printPost(c, post, usernames, showIds)
printPost(c, post, usernames, showIds, showTimestamp)
}

if follow {
Expand All @@ -176,7 +206,7 @@ func postListCmdF(c client.Client, cmd *cobra.Command, args []string) error {
fmt.Println("Error parsing incoming post: " + err.Error())
}
if post.ChannelId == channel.Id {
printPost(c, post, usernames, showIds)
printPost(c, post, usernames, showIds, showTimestamp)
}
}
}
Expand Down
194 changes: 194 additions & 0 deletions commands/post_e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

package commands

import (
"github.com/spf13/cobra"

"github.com/mattermost/mmctl/v6/client"
"github.com/mattermost/mmctl/v6/printer"

"github.com/mattermost/mattermost-server/v6/model"
)

func (s *MmctlE2ETestSuite) TestPostListCmd() {
s.SetupTestHelper().InitBasic()

var createNewChannelAndPosts = func() (string, *model.Post, *model.Post) {
channelName := model.NewRandomString(10)
channelDisplayName := "channelDisplayName"

channel, err := s.th.App.CreateChannel(s.th.Context, &model.Channel{Name: channelName, DisplayName: channelDisplayName, Type: model.ChannelTypeOpen, TeamId: s.th.BasicTeam.Id}, false)
s.Require().Nil(err)

post1, err := s.th.App.CreatePost(s.th.Context, &model.Post{Message: model.NewRandomString(15), UserId: s.th.BasicUser.Id, ChannelId: channel.Id}, channel, false, false)
s.Require().Nil(err)

post2, err := s.th.App.CreatePost(s.th.Context, &model.Post{Message: model.NewRandomString(15), UserId: s.th.BasicUser.Id, ChannelId: channel.Id}, channel, false, false)
s.Require().Nil(err)

return channelName, post1, post2
}

s.RunForSystemAdminAndLocal("List all posts for a channel", func(c client.Client) {
printer.Clean()

teamName := s.th.BasicTeam.Name
channelName, post1, post2 := createNewChannelAndPosts()

cmd := &cobra.Command{}
cmd.Flags().Int("number", 2, "")

err := postListCmdF(c, cmd, []string{teamName + ":" + channelName})
s.Require().Nil(err)
s.Equal(2, len(printer.GetLines()))

printedPost1, ok := printer.GetLines()[0].(*model.Post)
s.Require().True(ok)
s.Require().Equal(printedPost1.Message, post1.Message)

printedPost2, ok := printer.GetLines()[1].(*model.Post)
s.Require().True(ok)
s.Require().Equal(printedPost2.Message, post2.Message)
s.Len(printer.GetErrorLines(), 0)
})

s.Run("List all posts for a channel without permissions", func() {
printer.Clean()

teamName := s.th.BasicTeam.Name
channelName, _, _ := createNewChannelAndPosts()

cmd := &cobra.Command{}
cmd.Flags().Int("number", 2, "")

err := postListCmdF(s.th.Client, cmd, []string{teamName + ":" + channelName})
s.Require().NotNil(err)
s.Require().Contains(err.Error(), "You do not have the appropriate permissions.")
})

s.RunForSystemAdminAndLocal("List all posts for a channel with since flag", func(c client.Client) {
printer.Clean()

ISO8601ValidString := "2006-01-02T15:04:05-07:00"
teamName := s.th.BasicTeam.Name
channelName, post1, post2 := createNewChannelAndPosts()

cmd := &cobra.Command{}
cmd.Flags().String("since", ISO8601ValidString, "")

err := postListCmdF(c, cmd, []string{teamName + ":" + channelName})
s.Require().Nil(err)
s.Equal(2, len(printer.GetLines()))

printedPost1, ok := printer.GetLines()[0].(*model.Post)
s.Require().True(ok)
s.Require().Equal(printedPost1.Message, post1.Message)

printedPost2, ok := printer.GetLines()[1].(*model.Post)
s.Require().True(ok)
s.Require().Equal(printedPost2.Message, post2.Message)
s.Len(printer.GetErrorLines(), 0)
})

s.Run("List all posts for a channel with since flag without permissions", func() {
printer.Clean()

ISO8601ValidString := "2006-01-02T15:04:05-07:00"
teamName := s.th.BasicTeam.Name
channelName, _, _ := createNewChannelAndPosts()

cmd := &cobra.Command{}
cmd.Flags().String("since", ISO8601ValidString, "")

err := postListCmdF(s.th.Client, cmd, []string{teamName + ":" + channelName})
s.Require().NotNil(err)
s.Require().Contains(err.Error(), "You do not have the appropriate permissions.")
})
}

func (s *MmctlE2ETestSuite) TestPostCreateCmd() {
s.SetupTestHelper().InitBasic()

s.Run("Create a post for System Admin Client", func() {
printer.Clean()

msgArg := "some text"

cmd := &cobra.Command{}
cmd.Flags().String("message", msgArg, "")

err := postCreateCmdF(s.th.SystemAdminClient, cmd, []string{s.th.BasicTeam.Name + ":" + s.th.BasicChannel.Name})
s.Require().Nil(err)
s.Len(printer.GetErrorLines(), 0)
})

s.Run("Create a post for Client", func() {
printer.Clean()

msgArg := "some text"

cmd := &cobra.Command{}
cmd.Flags().String("message", msgArg, "")

err := postCreateCmdF(s.th.Client, cmd, []string{s.th.BasicTeam.Name + ":" + s.th.BasicChannel.Name})
s.Require().Nil(err)
s.Len(printer.GetErrorLines(), 0)
})

s.Run("Create a post for Local Client should fail", func() {
printer.Clean()

msgArg := "some text"

cmd := &cobra.Command{}
cmd.Flags().String("message", msgArg, "")

err := postCreateCmdF(s.th.LocalClient, cmd, []string{s.th.BasicTeam.Name + ":" + s.th.BasicChannel.Name})
s.Require().NotNil(err)
s.Len(printer.GetErrorLines(), 0)
})

s.Run("Reply to a an existing post for System Admin Client", func() {
printer.Clean()

msgArg := "some text"

cmd := &cobra.Command{}
cmd.Flags().String("message", msgArg, "")
cmd.Flags().String("reply-to", s.th.BasicPost.Id, "")

err := postCreateCmdF(s.th.SystemAdminClient, cmd, []string{s.th.BasicTeam.Name + ":" + s.th.BasicChannel.Name})
s.Require().Nil(err)
s.Len(printer.GetErrorLines(), 0)
})

s.Run("Reply to a an existing post for Client", func() {
printer.Clean()

msgArg := "some text"

cmd := &cobra.Command{}
cmd.Flags().String("message", msgArg, "")
cmd.Flags().String("reply-to", s.th.BasicPost.Id, "")

err := postCreateCmdF(s.th.Client, cmd, []string{s.th.BasicTeam.Name + ":" + s.th.BasicChannel.Name})
s.Require().Nil(err)
s.Len(printer.GetErrorLines(), 0)
})

s.Run("Reply to a an existing post for Local Client should fail", func() {
printer.Clean()

msgArg := "some text"

cmd := &cobra.Command{}
cmd.Flags().String("message", msgArg, "")
cmd.Flags().String("reply-to", s.th.BasicPost.Id, "")

err := postCreateCmdF(s.th.LocalClient, cmd, []string{s.th.BasicTeam.Name + ":" + s.th.BasicChannel.Name})
s.Require().NotNil(err)
s.Len(printer.GetErrorLines(), 0)
})
}
Loading

0 comments on commit bf36650

Please sign in to comment.