From a46840e96be88ff7d684338003e02ee536b18ee0 Mon Sep 17 00:00:00 2001 From: Martin Kraft Date: Mon, 11 Jul 2022 08:58:40 -0400 Subject: [PATCH] MM-45395: Exclude bot and webhook posts from Top Team Channels. Exclude webhook posts from My Top Channels. (#20599) * MM-45395: Exclude bot and webhook posts from Top Team Channels. Exclude webhook posts from My Top Channels. * MM-45395: Adds missing error test. * MM-45395: Adds missing whitespace. --- app/channel_test.go | 48 ++++++++++++++++++++++++++++++++ store/sqlstore/channel_store.go | 31 +++++++++++++-------- store/storetest/channel_store.go | 20 +++++++++++++ 3 files changed, 88 insertions(+), 11 deletions(-) diff --git a/app/channel_test.go b/app/channel_test.go index 1561d5143c..49585bf754 100644 --- a/app/channel_test.go +++ b/app/channel_test.go @@ -2382,7 +2382,31 @@ func TestGetTopChannelsForTeamSince(t *testing.T) { defer th.TearDown() channel2 := th.CreateChannel(th.BasicTeam) + + // add a bot post to ensure it's not counted + _, err := th.Server.Store.Post().Save(&model.Post{ + Message: "hello from a bot", + ChannelId: channel2.Id, + UserId: th.BasicUser.Id, + Props: model.StringInterface{ + "from_bot": true, + }, + }) + require.NoError(t, err) + channel3 := th.CreatePrivateChannel(th.BasicTeam) + + // add a webhook post to ensure it's not counted + _, err = th.Server.Store.Post().Save(&model.Post{ + Message: "hello from a webhook", + ChannelId: channel3.Id, + UserId: th.BasicUser.Id, + Props: model.StringInterface{ + "from_webhook": true, + }, + }) + require.NoError(t, err) + channel4 := th.CreatePrivateChannel(th.BasicTeam) channel5 := th.CreateChannel(th.BasicTeam) channel6 := th.CreatePrivateChannel(th.BasicTeam) @@ -2436,7 +2460,31 @@ func TestGetTopChannelsForUserSince(t *testing.T) { defer th.TearDown() channel2 := th.CreateChannel(th.BasicTeam) + + // add a bot post to ensure it's not counted + _, err := th.Server.Store.Post().Save(&model.Post{ + Message: "hello from a bot", + ChannelId: channel2.Id, + UserId: th.BasicUser.Id, + Props: model.StringInterface{ + "from_bot": true, + }, + }) + require.NoError(t, err) + channel3 := th.CreatePrivateChannel(th.BasicTeam) + + // add a webhook post to ensure it's not counted + _, err = th.Server.Store.Post().Save(&model.Post{ + Message: "hello from a webhook", + ChannelId: channel3.Id, + UserId: th.BasicUser.Id, + Props: model.StringInterface{ + "from_webhook": true, + }, + }) + require.NoError(t, err) + channel4 := th.CreatePrivateChannel(th.BasicTeam) channel5 := th.CreateChannel(th.BasicTeam) channel6 := th.CreatePrivateChannel(th.BasicTeam) diff --git a/store/sqlstore/channel_store.go b/store/sqlstore/channel_store.go index 60110e7074..04bba25a42 100644 --- a/store/sqlstore/channel_store.go +++ b/store/sqlstore/channel_store.go @@ -4158,8 +4158,8 @@ func (s SqlChannelStore) GetTeamForChannel(channelID string) (*model.Team, error func (s SqlChannelStore) GetTopChannelsForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopChannelList, error) { channels := make([]*model.TopChannel, 0) var args []any - postgresPropQuery := `AND (Posts.Props ->> 'from_bot' IS NULL OR Posts.Props ->> 'from_bot' = 'false')` - mySqlPropsQuery := `AND (JSON_EXTRACT(Posts.Props, '$.from_bot') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_bot') = 'false')` + postgresPropQuery := `AND (Posts.Props ->> 'from_bot' IS NULL OR Posts.Props ->> 'from_bot' = 'false') AND (Posts.Props ->> 'from_webhook' IS NULL OR Posts.Props ->> 'from_webhook' = 'false')` + mySqlPropsQuery := `AND (JSON_EXTRACT(Posts.Props, '$.from_bot') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_bot') = 'false') AND (JSON_EXTRACT(Posts.Props, '$.from_webhook') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_webhook') = 'false')` query := ` SELECT @@ -4260,6 +4260,13 @@ func (s SqlChannelStore) GetTopChannelsForUserSince(userID string, teamID string var args []any var query string + var propsQuery string + if s.DriverName() == model.DatabaseDriverMysql { + propsQuery = `AND (JSON_EXTRACT(Posts.Props, '$.from_bot') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_bot') = 'false') AND (JSON_EXTRACT(Posts.Props, '$.from_webhook') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_webhook') = 'false')` + } else if s.DriverName() == model.DatabaseDriverPostgres { + propsQuery = `AND (Posts.Props ->> 'from_bot' IS NULL OR Posts.Props ->> 'from_bot' = 'false') AND (Posts.Props ->> 'from_webhook' IS NULL OR Posts.Props ->> 'from_webhook' = 'false')` + } + query = ` SELECT Posts.ChannelId AS ID, @@ -4269,17 +4276,19 @@ func (s SqlChannelStore) GetTopChannelsForUserSince(userID string, teamID string Channels.TeamId AS TeamID, count(Posts.Id) AS MessageCount FROM - Posts + Posts LEFT JOIN Channels on Posts.ChannelId = Channels.Id LEFT JOIN ChannelMembers on Posts.ChannelId = ChannelMembers.ChannelId - WHERE - Posts.DeleteAt = 0 + WHERE + Posts.DeleteAt = 0 AND Posts.CreateAt > ? AND Posts.Type = '' AND Posts.UserID = ? AND Channels.DeleteAt = 0 - AND (Channels.Type = 'O' OR Channels.Type = 'P') - AND ChannelMembers.UserId = ?` + AND (Channels.Type = 'O' OR Channels.Type = 'P') + AND ChannelMembers.UserId = ? ` + + query += propsQuery args = []any{since, userID, userID} @@ -4290,13 +4299,13 @@ func (s SqlChannelStore) GetTopChannelsForUserSince(userID string, teamID string } query += ` - Group By + Group By Posts.ChannelId, Channels.Type, Channels.DisplayName, Channels.Name, Channels.TeamId - ORDER BY + ORDER BY MessageCount DESC, Name ASC LIMIT ? @@ -4323,14 +4332,14 @@ func (s SqlChannelStore) PostCountsByDuration(channelIDs []string, sinceUnixMill } else { unixSelect = `DATE_FORMAT(CONVERT_TZ(FROM_UNIXTIME(Posts.CreateAt / 1000), 'GMT', '` + loc + `'),'%Y-%m-%dT%H') AS duration` } - propsQuery = `(JSON_EXTRACT(Posts.Props, '$.from_bot') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_bot') = 'false')` + propsQuery = `(JSON_EXTRACT(Posts.Props, '$.from_bot') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_bot') = 'false') AND (JSON_EXTRACT(Posts.Props, '$.from_webhook') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_webhook') = 'false')` } else if s.DriverName() == model.DatabaseDriverPostgres { if duration == model.PostsByDay { unixSelect = fmt.Sprintf(`TO_CHAR(TO_TIMESTAMP(Posts.CreateAt / 1000) AT TIME ZONE '%s', 'YYYY-MM-DD') AS duration`, loc) } else { unixSelect = fmt.Sprintf(`TO_CHAR(TO_TIMESTAMP(Posts.CreateAt / 1000) AT TIME ZONE '%s', 'YYYY-MM-DD"T"HH24') AS duration`, loc) } - propsQuery = `(Posts.Props ->> 'from_bot' IS NULL OR Posts.Props ->> 'from_bot' = 'false')` + propsQuery = `(Posts.Props ->> 'from_bot' IS NULL OR Posts.Props ->> 'from_bot' = 'false') AND (Posts.Props ->> 'from_webhook' IS NULL OR Posts.Props ->> 'from_webhook' = 'false')` } query := sq. Select("Posts.ChannelId AS channelid", unixSelect, "count(Posts.Id) AS postcount"). diff --git a/store/storetest/channel_store.go b/store/storetest/channel_store.go index bb2dae3451..9f20aa7a17 100644 --- a/store/storetest/channel_store.go +++ b/store/storetest/channel_store.go @@ -7935,6 +7935,26 @@ func testChannelPostCountsByDuration(t *testing.T, ss store.Store) { }) require.NoError(t, err) + _, err = ss.Post().Save(&model.Post{ + UserId: userID, + ChannelId: channel.Id, + Message: "test", + Props: model.StringInterface{ + "from_bot": true, + }, + }) + require.NoError(t, err) + + _, err = ss.Post().Save(&model.Post{ + UserId: userID, + ChannelId: channel.Id, + Message: "test", + Props: model.StringInterface{ + "from_webhook": true, + }, + }) + require.NoError(t, err) + dpc, err := ss.Channel().PostCountsByDuration([]string{channelSaved.Id}, 0, &userID, model.PostsByDay, time.Now().Location()) require.NoError(t, err) require.Len(t, dpc, 1)