Remove insights (#23952)
* removed server side * Updated store layer * unused import * Updated autogenerated code template * Updated tests * lint fix * unused translations * webapp side * Updated i18n * lint fix: * type fix * Updated snapshots * Removed insights from API specs * updated e2e * Updated e2e tests * Updated e2e tests * Removed insights tests * Removed Insights as possible channel to load in sidebar from test * Removed more insights tests * More e2e fixed * More cleanup * Lint * More cleanup in client4 and boards api * More cleanup * Fixes * lint fix --------- Co-authored-by: maria.nunez <maria.nunez@mattermost.com> Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
e37459cd00
Коммит
26617fcbdc
@@ -285,12 +285,6 @@ type AppIface interface {
|
||||
// PopulateWebConnConfig checks if the connection id already exists in the hub,
|
||||
// and if so, accordingly populates the other fields of the webconn.
|
||||
PopulateWebConnConfig(s *model.Session, cfg *platform.WebConnConfig, seqVal string) (*platform.WebConnConfig, error)
|
||||
// PostCountsByDuration returns the post counts for the given channels, grouped by day, starting at the given time.
|
||||
// Unless one is specifically itending to omit results from part of the calendar day, it will typically makes the most sense to
|
||||
// use a sinceUnixMillis parameter value as returned by model.GetStartOfDayMillis.
|
||||
//
|
||||
// WARNING: PostCountsByDuration PERFORMS NO AUTHORIZATION CHECKS ON THE GIVEN CHANNELS.
|
||||
PostCountsByDuration(c request.CTX, channelIDs []string, sinceUnixMillis int64, userID *string, grouping model.PostCountGrouping, groupingLocation *time.Location) ([]*model.DurationPostCount, *model.AppError)
|
||||
// PromoteGuestToUser Convert user's roles and all his membership's roles from
|
||||
// guest roles to regular user roles.
|
||||
PromoteGuestToUser(c *request.Context, user *model.User, requestorId string) *model.AppError
|
||||
@@ -696,7 +690,6 @@ type AppIface interface {
|
||||
GetMemberCountsByGroup(ctx context.Context, channelID string, includeTimezones bool) ([]*model.ChannelMemberCountByGroup, *model.AppError)
|
||||
GetMessageForNotification(post *model.Post, translateFunc i18n.TranslateFunc) string
|
||||
GetMultipleEmojiByName(c request.CTX, names []string) ([]*model.Emoji, *model.AppError)
|
||||
GetNewTeamMembersSince(c request.CTX, teamID string, opts *model.InsightsOpts) (*model.NewTeamMembersList, int64, *model.AppError)
|
||||
GetNewUsersForTeamPage(teamID string, page, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError)
|
||||
GetNextPostIdFromPostList(postList *model.PostList, collapsedThreads bool) string
|
||||
GetNotificationNameFormat(user *model.User) string
|
||||
@@ -817,15 +810,6 @@ type AppIface interface {
|
||||
GetThreadMembershipsForUser(userID, teamID string) ([]*model.ThreadMembership, error)
|
||||
GetThreadsForUser(userID, teamID string, options model.GetUserThreadsOpts) (*model.Threads, *model.AppError)
|
||||
GetTokenById(token string) (*model.Token, *model.AppError)
|
||||
GetTopChannelsForTeamSince(c request.CTX, teamID, userID string, opts *model.InsightsOpts) (*model.TopChannelList, *model.AppError)
|
||||
GetTopChannelsForUserSince(c request.CTX, userID, teamID string, opts *model.InsightsOpts) (*model.TopChannelList, *model.AppError)
|
||||
GetTopDMsForUserSince(userID string, opts *model.InsightsOpts) (*model.TopDMList, *model.AppError)
|
||||
GetTopInactiveChannelsForTeamSince(c request.CTX, teamID, userID string, opts *model.InsightsOpts) (*model.TopInactiveChannelList, *model.AppError)
|
||||
GetTopInactiveChannelsForUserSince(c request.CTX, teamID, userID string, opts *model.InsightsOpts) (*model.TopInactiveChannelList, *model.AppError)
|
||||
GetTopReactionsForTeamSince(teamID string, userID string, opts *model.InsightsOpts) (*model.TopReactionList, *model.AppError)
|
||||
GetTopReactionsForUserSince(userID string, teamID string, opts *model.InsightsOpts) (*model.TopReactionList, *model.AppError)
|
||||
GetTopThreadsForTeamSince(c request.CTX, teamID, userID string, opts *model.InsightsOpts) (*model.TopThreadList, *model.AppError)
|
||||
GetTopThreadsForUserSince(c request.CTX, teamID, userID string, opts *model.InsightsOpts) (*model.TopThreadList, *model.AppError)
|
||||
GetTrueUpProfile() (map[string]any, error)
|
||||
GetUploadSession(c request.CTX, uploadId string) (*model.UploadSession, *model.AppError)
|
||||
GetUploadSessionsForUser(userID string) ([]*model.UploadSession, *model.AppError)
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/plugin"
|
||||
@@ -3518,66 +3517,3 @@ func (s *Server) getDirectChannel(c request.CTX, userID, otherUserID string) (*m
|
||||
|
||||
return channel, nil
|
||||
}
|
||||
|
||||
func (a *App) GetTopChannelsForTeamSince(c request.CTX, teamID, userID string, opts *model.InsightsOpts) (*model.TopChannelList, *model.AppError) {
|
||||
if !a.Config().FeatureFlags.InsightsEnabled {
|
||||
return nil, model.NewAppError("GetTopChannelsForTeamSince", "api.insights.feature_disabled", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
topChannels, err := a.Srv().Store().Channel().GetTopChannelsForTeamSince(teamID, userID, opts.StartUnixMilli, opts.Page*opts.PerPage, opts.PerPage)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetTopChannelsForTeamSince", model.NoTranslation, nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return topChannels, nil
|
||||
}
|
||||
|
||||
func (a *App) GetTopChannelsForUserSince(c request.CTX, userID, teamID string, opts *model.InsightsOpts) (*model.TopChannelList, *model.AppError) {
|
||||
if !a.Config().FeatureFlags.InsightsEnabled {
|
||||
return nil, model.NewAppError("GetTopChannelsForUserSince", "api.insights.feature_disabled", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
topChannels, err := a.Srv().Store().Channel().GetTopChannelsForUserSince(userID, teamID, opts.StartUnixMilli, opts.Page*opts.PerPage, opts.PerPage)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetTopChannelsForUserSince", model.NoTranslation, nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return topChannels, nil
|
||||
}
|
||||
|
||||
// PostCountsByDuration returns the post counts for the given channels, grouped by day, starting at the given time.
|
||||
// Unless one is specifically itending to omit results from part of the calendar day, it will typically makes the most sense to
|
||||
// use a sinceUnixMillis parameter value as returned by model.GetStartOfDayMillis.
|
||||
//
|
||||
// WARNING: PostCountsByDuration PERFORMS NO AUTHORIZATION CHECKS ON THE GIVEN CHANNELS.
|
||||
func (a *App) PostCountsByDuration(c request.CTX, channelIDs []string, sinceUnixMillis int64, userID *string, grouping model.PostCountGrouping, groupingLocation *time.Location) ([]*model.DurationPostCount, *model.AppError) {
|
||||
if !a.Config().FeatureFlags.InsightsEnabled {
|
||||
return nil, model.NewAppError("PostCountsByDuration", "api.insights.feature_disabled", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
postCountByDay, err := a.Srv().Store().Channel().PostCountsByDuration(channelIDs, sinceUnixMillis, userID, grouping, groupingLocation)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("PostCountsByDuration", model.NoTranslation, nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return postCountByDay, nil
|
||||
}
|
||||
|
||||
func (a *App) GetTopInactiveChannelsForTeamSince(c request.CTX, teamID, userID string, opts *model.InsightsOpts) (*model.TopInactiveChannelList, *model.AppError) {
|
||||
if !a.Config().FeatureFlags.InsightsEnabled {
|
||||
return nil, model.NewAppError("GetTopChannelsForTeamSince", "api.insights.feature_disabled", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
topChannels, err := a.Srv().Store().Channel().GetTopInactiveChannelsForTeamSince(teamID, userID, opts.StartUnixMilli, opts.Page*opts.PerPage, opts.PerPage)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetTopInactiveChannelsForTeamSince", model.NoTranslation, nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return topChannels, nil
|
||||
}
|
||||
|
||||
func (a *App) GetTopInactiveChannelsForUserSince(c request.CTX, teamID, userID string, opts *model.InsightsOpts) (*model.TopInactiveChannelList, *model.AppError) {
|
||||
if !a.Config().FeatureFlags.InsightsEnabled {
|
||||
return nil, model.NewAppError("GetTopChannelsForUserSince", "api.insights.feature_disabled", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
topChannels, err := a.Srv().Store().Channel().GetTopInactiveChannelsForUserSince(teamID, userID, opts.StartUnixMilli, opts.Page*opts.PerPage, opts.PerPage)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetTopInactiveChannelsForUserSince", model.NoTranslation, nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return topChannels, nil
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
@@ -2495,523 +2494,3 @@ func TestIsCRTEnabledForUser(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTopChannelsForTeamSince(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.ConfigStore.SetReadOnlyFF(false)
|
||||
defer th.ConfigStore.SetReadOnlyFF(true)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
|
||||
channel2 := th.CreateChannel(th.Context, 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.Context, 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)
|
||||
|
||||
// add an oauth app post to ensure it's not counted
|
||||
_, err = th.Server.Store().Post().Save(&model.Post{
|
||||
Message: "hello from an ouath app",
|
||||
ChannelId: channel3.Id,
|
||||
UserId: th.BasicUser.Id,
|
||||
Props: model.StringInterface{
|
||||
"from_oauth_app": true,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// add a plugin post to ensure it's not counted
|
||||
_, err = th.Server.Store().Post().Save(&model.Post{
|
||||
Message: "hello from a plugin",
|
||||
ChannelId: channel3.Id,
|
||||
UserId: th.BasicUser.Id,
|
||||
Props: model.StringInterface{
|
||||
"from_plugin": true,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// add a system post to ensure it's not counted
|
||||
_, err = th.Server.Store().Post().Save(&model.Post{
|
||||
Message: "system message",
|
||||
Type: "system_join_channel",
|
||||
ChannelId: channel3.Id,
|
||||
UserId: th.BasicUser.Id,
|
||||
Props: model.StringInterface{
|
||||
"from_oauth_app": true,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
channel4 := th.CreatePrivateChannel(th.Context, th.BasicTeam)
|
||||
channel5 := th.CreateChannel(th.Context, th.BasicTeam)
|
||||
channel6 := th.CreatePrivateChannel(th.Context, th.BasicTeam)
|
||||
th.AddUserToChannel(th.BasicUser, channel2)
|
||||
th.AddUserToChannel(th.BasicUser, channel3)
|
||||
th.AddUserToChannel(th.BasicUser, channel4)
|
||||
th.AddUserToChannel(th.BasicUser, channel5)
|
||||
th.AddUserToChannel(th.BasicUser, channel6)
|
||||
|
||||
channels := [6]*model.Channel{th.BasicChannel, channel2, channel3, channel4, channel5, channel6}
|
||||
|
||||
i := len(channels)
|
||||
for _, channel := range channels {
|
||||
for j := i; j > 0; j-- {
|
||||
th.CreatePost(channel)
|
||||
}
|
||||
i--
|
||||
}
|
||||
|
||||
expectedTopChannels := []struct {
|
||||
ID string
|
||||
MessageCount int64
|
||||
}{
|
||||
{ID: th.BasicChannel.Id, MessageCount: 7},
|
||||
{ID: channel2.Id, MessageCount: 5},
|
||||
{ID: channel3.Id, MessageCount: 4},
|
||||
{ID: channel4.Id, MessageCount: 3},
|
||||
{ID: channel5.Id, MessageCount: 2},
|
||||
}
|
||||
|
||||
timeRange, _ := model.GetStartOfDayForTimeRange(model.TimeRangeToday, time.Now().Location())
|
||||
|
||||
t.Run("get-top-channels-for-team-since", func(t *testing.T) {
|
||||
topChannels, err := th.App.GetTopChannelsForTeamSince(th.Context, th.BasicChannel.TeamId, th.BasicUser.Id, &model.InsightsOpts{StartUnixMilli: timeRange.UnixMilli(), Page: 0, PerPage: 5})
|
||||
require.Nil(t, err)
|
||||
|
||||
for i, channel := range topChannels.Items {
|
||||
assert.Equal(t, expectedTopChannels[i].ID, channel.ID)
|
||||
assert.Equal(t, expectedTopChannels[i].MessageCount, channel.MessageCount)
|
||||
}
|
||||
|
||||
topChannels, err = th.App.GetTopChannelsForTeamSince(th.Context, th.BasicChannel.TeamId, th.BasicUser.Id, &model.InsightsOpts{StartUnixMilli: timeRange.UnixMilli(), Page: 1, PerPage: 5})
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, channel6.Id, topChannels.Items[0].ID)
|
||||
assert.Equal(t, int64(1), topChannels.Items[0].MessageCount)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTopChannelsForUserSince(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.ConfigStore.SetReadOnlyFF(false)
|
||||
defer th.ConfigStore.SetReadOnlyFF(true)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
|
||||
channel2 := th.CreateChannel(th.Context, 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.Context, 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.Context, th.BasicTeam)
|
||||
channel5 := th.CreateChannel(th.Context, th.BasicTeam)
|
||||
channel6 := th.CreatePrivateChannel(th.Context, th.BasicTeam)
|
||||
th.AddUserToChannel(th.BasicUser, channel2)
|
||||
th.AddUserToChannel(th.BasicUser, channel3)
|
||||
th.AddUserToChannel(th.BasicUser, channel4)
|
||||
th.AddUserToChannel(th.BasicUser, channel5)
|
||||
th.AddUserToChannel(th.BasicUser, channel6)
|
||||
|
||||
channels := [6]*model.Channel{th.BasicChannel, channel2, channel3, channel4, channel5, channel6}
|
||||
|
||||
i := len(channels)
|
||||
for _, channel := range channels {
|
||||
for j := i; j > 0; j-- {
|
||||
th.CreatePost(channel)
|
||||
}
|
||||
i--
|
||||
}
|
||||
|
||||
expectedTopChannels := []struct {
|
||||
ID string
|
||||
MessageCount int64
|
||||
}{
|
||||
{ID: th.BasicChannel.Id, MessageCount: 7},
|
||||
{ID: channel2.Id, MessageCount: 5},
|
||||
{ID: channel3.Id, MessageCount: 4},
|
||||
{ID: channel4.Id, MessageCount: 3},
|
||||
{ID: channel5.Id, MessageCount: 2},
|
||||
}
|
||||
|
||||
timeRange, _ := model.GetStartOfDayForTimeRange(model.TimeRangeToday, time.Now().Location())
|
||||
|
||||
t.Run("get-top-channels-for-user-since", func(t *testing.T) {
|
||||
topChannels, err := th.App.GetTopChannelsForUserSince(th.Context, th.BasicUser.Id, "", &model.InsightsOpts{StartUnixMilli: timeRange.UnixMilli(), Page: 0, PerPage: 5})
|
||||
require.Nil(t, err)
|
||||
|
||||
for i, channel := range topChannels.Items {
|
||||
assert.Equal(t, expectedTopChannels[i].ID, channel.ID)
|
||||
assert.Equal(t, expectedTopChannels[i].MessageCount, channel.MessageCount)
|
||||
}
|
||||
|
||||
topChannels, err = th.App.GetTopChannelsForUserSince(th.Context, th.BasicUser.Id, th.BasicChannel.TeamId, &model.InsightsOpts{StartUnixMilli: timeRange.UnixMilli(), Page: 1, PerPage: 5})
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, channel6.Id, topChannels.Items[0].ID)
|
||||
assert.Equal(t, int64(1), topChannels.Items[0].MessageCount)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPostCountsByDuration(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.ConfigStore.SetReadOnlyFF(false)
|
||||
defer th.ConfigStore.SetReadOnlyFF(true)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
|
||||
channel2 := th.CreateChannel(th.Context, th.BasicTeam)
|
||||
channel3 := th.CreatePrivateChannel(th.Context, th.BasicTeam)
|
||||
channel4 := th.CreatePrivateChannel(th.Context, th.BasicTeam)
|
||||
channel5 := th.CreateChannel(th.Context, th.BasicTeam)
|
||||
channel6 := th.CreatePrivateChannel(th.Context, th.BasicTeam)
|
||||
defer func() {
|
||||
th.App.PermanentDeleteChannel(th.Context, channel2)
|
||||
th.App.PermanentDeleteChannel(th.Context, channel3)
|
||||
th.App.PermanentDeleteChannel(th.Context, channel4)
|
||||
th.App.PermanentDeleteChannel(th.Context, channel5)
|
||||
th.App.PermanentDeleteChannel(th.Context, channel6)
|
||||
}()
|
||||
th.AddUserToChannel(th.BasicUser, channel2)
|
||||
th.AddUserToChannel(th.BasicUser, channel3)
|
||||
th.AddUserToChannel(th.BasicUser, channel4)
|
||||
th.AddUserToChannel(th.BasicUser, channel5)
|
||||
th.AddUserToChannel(th.BasicUser, channel6)
|
||||
|
||||
channels := [6]*model.Channel{th.BasicChannel, channel2, channel3, channel4, channel5, channel6}
|
||||
channelIDs := []string{th.BasicChannel.Id, channel2.Id, channel3.Id, channel4.Id, channel5.Id, channel6.Id}
|
||||
|
||||
i := len(channels)
|
||||
for ci, channel := range channels {
|
||||
for j := i; j > 0; j-- {
|
||||
d1 := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).AddDate(0, 0, ci)
|
||||
d2 := time.Date(2009, time.November, 10, 9, 0, 0, 0, time.UTC).AddDate(0, 0, ci)
|
||||
_, err := th.App.CreatePost(th.Context, &model.Post{
|
||||
UserId: th.BasicUser.Id,
|
||||
ChannelId: channel.Id,
|
||||
CreateAt: d1.Unix() * 1000,
|
||||
}, channel, false, false)
|
||||
require.Nil(t, err)
|
||||
_, err = th.App.CreatePost(th.Context, &model.Post{
|
||||
UserId: th.BasicUser2.Id,
|
||||
ChannelId: channel.Id,
|
||||
CreateAt: d2.Unix() * 1000,
|
||||
}, channel, false, false)
|
||||
require.Nil(t, err)
|
||||
}
|
||||
i--
|
||||
}
|
||||
|
||||
expectedDayGrouping := map[string]map[string]int{
|
||||
"2009-11-10": {
|
||||
th.BasicChannel.Id: 6,
|
||||
},
|
||||
"2009-11-11": {
|
||||
channel2.Id: 5,
|
||||
},
|
||||
"2009-11-12": {
|
||||
channel3.Id: 4,
|
||||
},
|
||||
"2009-11-13": {
|
||||
channel4.Id: 3,
|
||||
},
|
||||
"2009-11-14": {
|
||||
channel5.Id: 2,
|
||||
},
|
||||
"2009-11-15": {
|
||||
channel6.Id: 1,
|
||||
},
|
||||
}
|
||||
|
||||
expectedHourGrouping := map[string]map[string]int{
|
||||
"2009-11-15T09": {
|
||||
channel6.Id: 1,
|
||||
},
|
||||
"2009-11-15T23": {
|
||||
channel6.Id: 1,
|
||||
},
|
||||
}
|
||||
|
||||
sinceUnixMillis := time.Date(2009, time.November, 9, 23, 0, 0, 0, time.UTC).UnixMilli()
|
||||
|
||||
t.Run("get-post-counts-by-day scoped by user, grouped by day", func(t *testing.T) {
|
||||
dailyPostCount, err := th.App.PostCountsByDuration(th.Context, channelIDs, sinceUnixMillis, &th.BasicUser.Id, model.PostsByDay, time.Now().UTC().Location())
|
||||
require.Nil(t, err)
|
||||
require.GreaterOrEqual(t, len(dailyPostCount), 6)
|
||||
|
||||
for _, item := range dailyPostCount {
|
||||
if strings.HasPrefix(item.Duration, "2009") {
|
||||
expectedCount := expectedDayGrouping[item.Duration][item.ChannelID]
|
||||
assert.Equal(t, expectedCount, item.PostCount)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("get-post-counts-by-day all users, grouped by day", func(t *testing.T) {
|
||||
dailyPostCount, err := th.App.PostCountsByDuration(th.Context, channelIDs, sinceUnixMillis, nil, model.PostsByDay, time.Now().UTC().Location())
|
||||
require.Nil(t, err)
|
||||
require.GreaterOrEqual(t, len(dailyPostCount), 6)
|
||||
|
||||
for _, item := range dailyPostCount {
|
||||
if strings.HasPrefix(item.Duration, "2009") {
|
||||
expectedCount := expectedDayGrouping[item.Duration][item.ChannelID]
|
||||
assert.Equal(t, expectedCount*2, item.PostCount)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("get-post-counts-by-day all users, grouped by hour", func(t *testing.T) {
|
||||
oneDaySince := time.Date(2009, time.November, 14, 23, 0, 0, 0, time.UTC).UnixMilli()
|
||||
dailyPostCount, err := th.App.PostCountsByDuration(th.Context, channelIDs, oneDaySince, nil, model.PostsByHour, time.Now().UTC().Location())
|
||||
require.Nil(t, err)
|
||||
require.GreaterOrEqual(t, len(dailyPostCount), 1)
|
||||
|
||||
for _, item := range dailyPostCount {
|
||||
if strings.HasPrefix(item.Duration, "2009") {
|
||||
expectedCount := expectedHourGrouping[item.Duration][item.ChannelID]
|
||||
assert.Equal(t, expectedCount, item.PostCount)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Top inactive channels
|
||||
|
||||
func TestGetTopInactiveChannelsForTeamSince(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.ConfigStore.SetReadOnlyFF(false)
|
||||
defer th.ConfigStore.SetReadOnlyFF(true)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
|
||||
channel2 := th.CreateChannel(th.Context, th.BasicTeam, WithCreateAt(1))
|
||||
channel3 := th.CreateChannel(th.Context, th.BasicTeam, WithCreateAt(1))
|
||||
channel4 := th.CreatePrivateChannel(th.Context, th.BasicTeam, WithCreateAt(1))
|
||||
channel5 := th.CreateChannel(th.Context, th.BasicTeam, WithCreateAt(1))
|
||||
channel6 := th.CreatePrivateChannel(th.Context, th.BasicTeam, WithCreateAt(1))
|
||||
th.AddUserToChannel(th.BasicUser, channel2)
|
||||
th.AddUserToChannel(th.BasicUser, channel3)
|
||||
th.AddUserToChannel(th.BasicUser, channel4)
|
||||
th.AddUserToChannel(th.BasicUser, channel5)
|
||||
th.AddUserToChannel(th.BasicUser, channel6)
|
||||
|
||||
// delete offtopic, town square, basicChannel channel - which interferes with 'least' active channel results
|
||||
offTopicChannel, appErr := th.App.GetChannelByName(th.Context, "off-topic", th.BasicTeam.Id, false)
|
||||
require.Nil(t, appErr, "Expected nil, didn't receive nil")
|
||||
appErr = th.App.PermanentDeleteChannel(th.Context, offTopicChannel)
|
||||
require.Nil(t, appErr)
|
||||
townSquareChannel, appErr := th.App.GetChannelByName(th.Context, "town-square", th.BasicTeam.Id, false)
|
||||
require.Nil(t, appErr, "Expected nil, didn't receive nil")
|
||||
appErr = th.App.PermanentDeleteChannel(th.Context, townSquareChannel)
|
||||
require.Nil(t, appErr)
|
||||
basicChannel, appErr := th.App.GetChannel(th.Context, th.BasicChannel.Id)
|
||||
require.Nil(t, appErr, "Expected nil, didn't receive nil")
|
||||
appErr = th.App.PermanentDeleteChannel(th.Context, basicChannel)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
// add a bot post to ensure it's 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)
|
||||
|
||||
// add a webhook post to ensure it's 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)
|
||||
|
||||
channels := [5]*model.Channel{channel2, channel3, channel4, channel5, channel6}
|
||||
|
||||
i := len(channels)
|
||||
for _, channel := range channels {
|
||||
for j := i; j > 0; j-- {
|
||||
th.CreatePost(channel)
|
||||
}
|
||||
i--
|
||||
}
|
||||
|
||||
expectedTopChannels := []struct {
|
||||
ID string
|
||||
MessageCount int64
|
||||
}{
|
||||
{ID: channel6.Id, MessageCount: 1},
|
||||
{ID: channel5.Id, MessageCount: 2},
|
||||
{ID: channel4.Id, MessageCount: 3},
|
||||
{ID: channel3.Id, MessageCount: 5},
|
||||
{ID: channel2.Id, MessageCount: 6},
|
||||
}
|
||||
|
||||
timeRange, _ := model.GetStartOfDayForTimeRange(model.TimeRangeToday, time.Now().Location())
|
||||
|
||||
t.Run("get-top-channels-for-team-since", func(t *testing.T) {
|
||||
topChannels, err := th.App.GetTopInactiveChannelsForTeamSince(th.Context, th.BasicChannel.TeamId, th.BasicUser.Id, &model.InsightsOpts{StartUnixMilli: timeRange.UnixMilli(), Page: 0, PerPage: 5})
|
||||
require.Nil(t, err)
|
||||
|
||||
for i, channel := range topChannels.Items {
|
||||
assert.Equal(t, expectedTopChannels[i].ID, channel.ID)
|
||||
assert.Equal(t, expectedTopChannels[i].MessageCount, channel.MessageCount)
|
||||
}
|
||||
|
||||
topChannels, err = th.App.GetTopInactiveChannelsForTeamSince(th.Context, th.BasicChannel.TeamId, th.BasicUser.Id, &model.InsightsOpts{StartUnixMilli: timeRange.UnixMilli(), Page: 1, PerPage: 4})
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, channel2.Id, topChannels.Items[0].ID)
|
||||
assert.Equal(t, int64(6), topChannels.Items[0].MessageCount)
|
||||
|
||||
// it simulates channel being created recently
|
||||
_ = th.CreatePrivateChannel(th.Context, th.BasicTeam)
|
||||
topChannels, err = th.App.GetTopInactiveChannelsForTeamSince(th.Context, th.BasicChannel.TeamId, th.BasicUser.Id, &model.InsightsOpts{StartUnixMilli: timeRange.UnixMilli(), Page: 0, PerPage: 6})
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 5, len(topChannels.Items))
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTopInactiveChannelsForUserSince(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.ConfigStore.SetReadOnlyFF(false)
|
||||
defer th.ConfigStore.SetReadOnlyFF(true)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
|
||||
// delete offtopic, town-square, th.basicchannel channels - which interferes with 'least' active channel results
|
||||
offTopicChannel, appErr := th.App.GetChannelByName(th.Context, "off-topic", th.BasicTeam.Id, false)
|
||||
require.Nil(t, appErr, "Expected nil, didn't receive nil")
|
||||
appErr = th.App.PermanentDeleteChannel(th.Context, offTopicChannel)
|
||||
require.Nil(t, appErr)
|
||||
townSquareChannel, appErr := th.App.GetChannelByName(th.Context, "town-square", th.BasicTeam.Id, false)
|
||||
require.Nil(t, appErr, "Expected nil, didn't receive nil")
|
||||
appErr = th.App.PermanentDeleteChannel(th.Context, townSquareChannel)
|
||||
require.Nil(t, appErr)
|
||||
basicChannel, appErr := th.App.GetChannel(th.Context, th.BasicChannel.Id)
|
||||
require.Nil(t, appErr, "Expected nil, didn't receive nil")
|
||||
appErr = th.App.PermanentDeleteChannel(th.Context, basicChannel)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
channel2 := th.CreateChannel(th.Context, th.BasicTeam, WithCreateAt(1))
|
||||
|
||||
// add a bot post to ensure it's 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.Context, th.BasicTeam, WithCreateAt(1))
|
||||
|
||||
// add a webhook post to ensure it's 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.Context, th.BasicTeam, WithCreateAt(1))
|
||||
channel5 := th.CreateChannel(th.Context, th.BasicTeam, WithCreateAt(1))
|
||||
channel6 := th.CreatePrivateChannel(th.Context, th.BasicTeam, WithCreateAt(1))
|
||||
th.AddUserToChannel(th.BasicUser, channel2)
|
||||
th.AddUserToChannel(th.BasicUser, channel3)
|
||||
th.AddUserToChannel(th.BasicUser, channel4)
|
||||
th.AddUserToChannel(th.BasicUser, channel5)
|
||||
th.AddUserToChannel(th.BasicUser, channel6)
|
||||
|
||||
channels := [5]*model.Channel{channel2, channel3, channel4, channel5, channel6}
|
||||
|
||||
i := len(channels)
|
||||
for _, channel := range channels {
|
||||
for j := i; j > 0; j-- {
|
||||
th.CreatePost(channel)
|
||||
}
|
||||
i--
|
||||
}
|
||||
|
||||
expectedTopChannels := []struct {
|
||||
ID string
|
||||
MessageCount int64
|
||||
}{
|
||||
{ID: channel6.Id, MessageCount: 1},
|
||||
{ID: channel5.Id, MessageCount: 2},
|
||||
{ID: channel4.Id, MessageCount: 3},
|
||||
{ID: channel3.Id, MessageCount: 5},
|
||||
{ID: channel2.Id, MessageCount: 6},
|
||||
}
|
||||
|
||||
timeRange, _ := model.GetStartOfDayForTimeRange(model.TimeRangeToday, time.Now().Location())
|
||||
|
||||
t.Run("get-top-channels-for-user-since", func(t *testing.T) {
|
||||
topChannels, err := th.App.GetTopInactiveChannelsForUserSince(th.Context, th.BasicChannel.TeamId, th.BasicUser.Id, &model.InsightsOpts{StartUnixMilli: timeRange.UnixMilli(), Page: 0, PerPage: 4})
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, len(topChannels.Items), 4)
|
||||
for i, channel := range topChannels.Items {
|
||||
assert.Equal(t, expectedTopChannels[i].ID, channel.ID)
|
||||
assert.Equal(t, expectedTopChannels[i].MessageCount, channel.MessageCount)
|
||||
}
|
||||
|
||||
topChannels, err = th.App.GetTopInactiveChannelsForUserSince(th.Context, th.BasicChannel.TeamId, th.BasicUser.Id, &model.InsightsOpts{StartUnixMilli: timeRange.UnixMilli(), Page: 1, PerPage: 4})
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, len(topChannels.Items), 1)
|
||||
assert.Equal(t, channel2.Id, topChannels.Items[0].ID)
|
||||
assert.Equal(t, int64(6), topChannels.Items[0].MessageCount)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -7381,28 +7381,6 @@ func (a *OpenTracingAppLayer) GetMultipleEmojiByName(c request.CTX, names []stri
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetNewTeamMembersSince(c request.CTX, teamID string, opts *model.InsightsOpts) (*model.NewTeamMembersList, int64, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetNewTeamMembersSince")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1, resultVar2 := a.app.GetNewTeamMembersSince(c, teamID, opts)
|
||||
|
||||
if resultVar2 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar2))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1, resultVar2
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetNewUsersForTeamPage(teamID string, page int, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetNewUsersForTeamPage")
|
||||
@@ -10291,204 +10269,6 @@ func (a *OpenTracingAppLayer) GetTokenById(token string) (*model.Token, *model.A
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetTopChannelsForTeamSince(c request.CTX, teamID string, userID string, opts *model.InsightsOpts) (*model.TopChannelList, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetTopChannelsForTeamSince")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetTopChannelsForTeamSince(c, teamID, userID, opts)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetTopChannelsForUserSince(c request.CTX, userID string, teamID string, opts *model.InsightsOpts) (*model.TopChannelList, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetTopChannelsForUserSince")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetTopChannelsForUserSince(c, userID, teamID, opts)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetTopDMsForUserSince(userID string, opts *model.InsightsOpts) (*model.TopDMList, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetTopDMsForUserSince")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetTopDMsForUserSince(userID, opts)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetTopInactiveChannelsForTeamSince(c request.CTX, teamID string, userID string, opts *model.InsightsOpts) (*model.TopInactiveChannelList, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetTopInactiveChannelsForTeamSince")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetTopInactiveChannelsForTeamSince(c, teamID, userID, opts)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetTopInactiveChannelsForUserSince(c request.CTX, teamID string, userID string, opts *model.InsightsOpts) (*model.TopInactiveChannelList, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetTopInactiveChannelsForUserSince")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetTopInactiveChannelsForUserSince(c, teamID, userID, opts)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetTopReactionsForTeamSince(teamID string, userID string, opts *model.InsightsOpts) (*model.TopReactionList, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetTopReactionsForTeamSince")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetTopReactionsForTeamSince(teamID, userID, opts)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetTopReactionsForUserSince(userID string, teamID string, opts *model.InsightsOpts) (*model.TopReactionList, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetTopReactionsForUserSince")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetTopReactionsForUserSince(userID, teamID, opts)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetTopThreadsForTeamSince(c request.CTX, teamID string, userID string, opts *model.InsightsOpts) (*model.TopThreadList, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetTopThreadsForTeamSince")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetTopThreadsForTeamSince(c, teamID, userID, opts)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetTopThreadsForUserSince(c request.CTX, teamID string, userID string, opts *model.InsightsOpts) (*model.TopThreadList, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetTopThreadsForUserSince")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetTopThreadsForUserSince(c, teamID, userID, opts)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetTotalUsersStats(viewRestrictions *model.ViewUsersRestrictions) (*model.UsersStats, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetTotalUsersStats")
|
||||
@@ -13413,28 +13193,6 @@ func (a *OpenTracingAppLayer) PostAddToChannelMessage(c request.CTX, user *model
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) PostCountsByDuration(c request.CTX, channelIDs []string, sinceUnixMillis int64, userID *string, grouping model.PostCountGrouping, groupingLocation *time.Location) ([]*model.DurationPostCount, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.PostCountsByDuration")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.PostCountsByDuration(c, channelIDs, sinceUnixMillis, userID, grouping, groupingLocation)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) PostPatchWithProxyRemovedFromImageURLs(patch *model.PostPatch) *model.PostPatch {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.PostPatchWithProxyRemovedFromImageURLs")
|
||||
|
||||
@@ -178,22 +178,17 @@ func TestPluginAPIGetUserPreferences(t *testing.T) {
|
||||
|
||||
preferences, err := api.GetPreferencesForUser(user1.Id)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 3, len(preferences))
|
||||
assert.Equal(t, 2, len(preferences))
|
||||
|
||||
assert.Equal(t, user1.Id, preferences[0].UserId)
|
||||
assert.Equal(t, model.PreferenceCategoryInsights, preferences[0].Category)
|
||||
assert.Equal(t, model.PreferenceNameInsights, preferences[0].Name)
|
||||
assert.Equal(t, "{\"insights_modal_viewed\":false}", preferences[0].Value)
|
||||
assert.Equal(t, model.PreferenceRecommendedNextSteps, preferences[0].Category)
|
||||
assert.Equal(t, "hide", preferences[0].Name)
|
||||
assert.Equal(t, "false", preferences[0].Value)
|
||||
|
||||
assert.Equal(t, user1.Id, preferences[1].UserId)
|
||||
assert.Equal(t, model.PreferenceRecommendedNextSteps, preferences[1].Category)
|
||||
assert.Equal(t, "hide", preferences[1].Name)
|
||||
assert.Equal(t, "false", preferences[1].Value)
|
||||
|
||||
assert.Equal(t, user1.Id, preferences[2].UserId)
|
||||
assert.Equal(t, model.PreferenceCategoryTutorialSteps, preferences[2].Category)
|
||||
assert.Equal(t, user1.Id, preferences[2].Name)
|
||||
assert.Equal(t, "0", preferences[2].Value)
|
||||
assert.Equal(t, model.PreferenceCategoryTutorialSteps, preferences[1].Category)
|
||||
assert.Equal(t, user1.Id, preferences[1].Name)
|
||||
assert.Equal(t, "0", preferences[1].Value)
|
||||
}
|
||||
|
||||
func TestPluginAPIDeleteUserPreferences(t *testing.T) {
|
||||
@@ -245,10 +240,9 @@ func TestPluginAPIDeleteUserPreferences(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
preferences, err = api.GetPreferencesForUser(user2.Id)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 3, len(preferences))
|
||||
assert.Equal(t, model.PreferenceCategoryInsights, preferences[0].Category)
|
||||
assert.Equal(t, model.PreferenceRecommendedNextSteps, preferences[1].Category)
|
||||
assert.Equal(t, model.PreferenceCategoryTutorialSteps, preferences[2].Category)
|
||||
assert.Equal(t, 2, len(preferences))
|
||||
assert.Equal(t, model.PreferenceRecommendedNextSteps, preferences[0].Category)
|
||||
assert.Equal(t, model.PreferenceCategoryTutorialSteps, preferences[1].Category)
|
||||
}
|
||||
|
||||
func TestPluginAPIUpdateUserPreferences(t *testing.T) {
|
||||
@@ -266,22 +260,16 @@ func TestPluginAPIUpdateUserPreferences(t *testing.T) {
|
||||
|
||||
preferences, err := api.GetPreferencesForUser(user1.Id)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 3, len(preferences))
|
||||
assert.Equal(t, 2, len(preferences))
|
||||
|
||||
assert.Equal(t, user1.Id, preferences[0].UserId)
|
||||
assert.Equal(t, model.PreferenceCategoryInsights, preferences[0].Category)
|
||||
assert.Equal(t, model.PreferenceNameInsights, preferences[0].Name)
|
||||
assert.Equal(t, "{\"insights_modal_viewed\":false}", preferences[0].Value)
|
||||
|
||||
assert.Equal(t, model.PreferenceRecommendedNextSteps, preferences[0].Category)
|
||||
assert.Equal(t, "hide", preferences[0].Name)
|
||||
assert.Equal(t, "false", preferences[0].Value)
|
||||
assert.Equal(t, user1.Id, preferences[1].UserId)
|
||||
assert.Equal(t, model.PreferenceRecommendedNextSteps, preferences[1].Category)
|
||||
assert.Equal(t, "hide", preferences[1].Name)
|
||||
assert.Equal(t, "false", preferences[1].Value)
|
||||
|
||||
assert.Equal(t, user1.Id, preferences[2].UserId)
|
||||
assert.Equal(t, model.PreferenceCategoryTutorialSteps, preferences[2].Category)
|
||||
assert.Equal(t, user1.Id, preferences[2].Name)
|
||||
assert.Equal(t, "0", preferences[2].Value)
|
||||
assert.Equal(t, model.PreferenceCategoryTutorialSteps, preferences[1].Category)
|
||||
assert.Equal(t, user1.Id, preferences[1].Name)
|
||||
assert.Equal(t, "0", preferences[1].Value)
|
||||
|
||||
preference := model.Preference{
|
||||
Name: user1.Id,
|
||||
@@ -296,8 +284,8 @@ func TestPluginAPIUpdateUserPreferences(t *testing.T) {
|
||||
preferences, err = api.GetPreferencesForUser(user1.Id)
|
||||
require.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 4, len(preferences))
|
||||
expectedCategories := []string{model.PreferenceCategoryTutorialSteps, model.PreferenceCategoryTheme, model.PreferenceRecommendedNextSteps, model.PreferenceCategoryInsights}
|
||||
assert.Equal(t, 3, len(preferences))
|
||||
expectedCategories := []string{model.PreferenceCategoryTutorialSteps, model.PreferenceCategoryTheme, model.PreferenceRecommendedNextSteps}
|
||||
for _, pref := range preferences {
|
||||
assert.Contains(t, expectedCategories, pref.Category)
|
||||
assert.Equal(t, user1.Id, pref.UserId)
|
||||
|
||||
@@ -2045,49 +2045,6 @@ func (a *App) GetEditHistoryForPost(postID string) ([]*model.Post, *model.AppErr
|
||||
return posts, nil
|
||||
}
|
||||
|
||||
func (a *App) GetTopThreadsForTeamSince(c request.CTX, teamID, userID string, opts *model.InsightsOpts) (*model.TopThreadList, *model.AppError) {
|
||||
if !a.Config().FeatureFlags.InsightsEnabled {
|
||||
return nil, model.NewAppError("GetTopChannelsForTeamSince", "app.insights.feature_disabled", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
topThreads, err := a.Srv().Store().Thread().GetTopThreadsForTeamSince(teamID, userID, opts.StartUnixMilli, opts.Page*opts.PerPage, opts.PerPage)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetTopChannelsForTeamSince", "app.post.get_top_threads_for_team_since.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
topThreadsWithEmbedAndImage, err := includeEmbedsAndImages(a, c, topThreads, userID)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetTopChannelsForTeamSince", "app.post.get_top_threads_for_team_since.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return topThreadsWithEmbedAndImage, nil
|
||||
}
|
||||
|
||||
func (a *App) GetTopThreadsForUserSince(c request.CTX, teamID, userID string, opts *model.InsightsOpts) (*model.TopThreadList, *model.AppError) {
|
||||
if !a.Config().FeatureFlags.InsightsEnabled {
|
||||
return nil, model.NewAppError("GetTopChannelsForTeamSince", "app.insights.feature_disabled", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
topThreads, err := a.Srv().Store().Thread().GetTopThreadsForUserSince(teamID, userID, opts.StartUnixMilli, opts.Page*opts.PerPage, opts.PerPage)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetTopChannelsForTeamSince", "app.post.get_top_threads_for_team_since.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
topThreadsWithEmbedAndImage, err := includeEmbedsAndImages(a, c, topThreads, userID)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetTopChannelsForUserSince", "app.post.get_top_threads_for_user_since.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return topThreadsWithEmbedAndImage, nil
|
||||
}
|
||||
|
||||
func (a *App) GetTopDMsForUserSince(userID string, opts *model.InsightsOpts) (*model.TopDMList, *model.AppError) {
|
||||
if !a.Config().FeatureFlags.InsightsEnabled {
|
||||
return nil, model.NewAppError("GetTopDMsForUserSince", "app.insights.feature_disabled", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
topDMs, err := a.Srv().Store().Post().GetTopDMsForUserSince(userID, opts.StartUnixMilli, opts.Page*opts.PerPage, opts.PerPage)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetTopDMsForUserSince", "app.post.get_top_dms_for_user_since.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return topDMs, nil
|
||||
}
|
||||
|
||||
func (a *App) SetPostReminder(postID, userID string, targetTime int64) *model.AppError {
|
||||
// Store the reminder in the DB
|
||||
reminder := &model.PostReminder{
|
||||
@@ -2285,15 +2242,3 @@ func (a *App) GetPostInfo(c request.CTX, postID string) (*model.PostInfo, *model
|
||||
}
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func includeEmbedsAndImages(a *App, c request.CTX, topThreadList *model.TopThreadList, userID string) (*model.TopThreadList, error) {
|
||||
for _, topThread := range topThreadList.Items {
|
||||
topThread.Post = a.PreparePostForClientWithEmbedsAndImages(c, topThread.Post, false, false, true)
|
||||
sanitizedPost, err := a.SanitizePostMetadataForUser(c, topThread.Post, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
topThread.Post = sanitizedPost
|
||||
}
|
||||
return topThreadList, nil
|
||||
}
|
||||
|
||||
@@ -3028,195 +3028,6 @@ func TestComputeLastAccessiblePostTime(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTopThreadsForTeamSince(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.ConfigStore.SetReadOnlyFF(false)
|
||||
defer th.ConfigStore.SetReadOnlyFF(true)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
|
||||
// create a public channel, a private channel
|
||||
channelPublic := th.CreateChannel(th.Context, th.BasicTeam)
|
||||
channelPrivate := th.CreatePrivateChannel(th.Context, th.BasicTeam)
|
||||
th.AddUserToChannel(th.BasicUser, channelPublic)
|
||||
th.AddUserToChannel(th.BasicUser, channelPrivate)
|
||||
th.AddUserToChannel(th.BasicUser2, channelPublic)
|
||||
|
||||
// create two threads: one in public channel, one in private with only basicUser1
|
||||
|
||||
rootPostPublicChannel, appErr := th.App.CreatePost(th.Context, &model.Post{
|
||||
UserId: th.BasicUser.Id,
|
||||
ChannelId: channelPublic.Id,
|
||||
Message: "root post",
|
||||
}, channelPublic, false, true)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
_, appErr = th.App.CreatePost(th.Context, &model.Post{
|
||||
UserId: th.BasicUser2.Id,
|
||||
ChannelId: channelPublic.Id,
|
||||
RootId: rootPostPublicChannel.Id,
|
||||
Message: fmt.Sprintf("@%s", th.BasicUser2.Username),
|
||||
}, channelPublic, false, true)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
rootPostPrivateChannel, appErr := th.App.CreatePost(th.Context, &model.Post{
|
||||
UserId: th.BasicUser.Id,
|
||||
ChannelId: channelPrivate.Id,
|
||||
Message: "root post",
|
||||
}, channelPrivate, false, true)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
_, appErr = th.App.CreatePost(th.Context, &model.Post{
|
||||
UserId: th.BasicUser.Id,
|
||||
ChannelId: channelPrivate.Id,
|
||||
RootId: rootPostPrivateChannel.Id,
|
||||
Message: fmt.Sprintf("@%s", th.BasicUser2.Username),
|
||||
}, channelPrivate, false, true)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
_, appErr = th.App.CreatePost(th.Context, &model.Post{
|
||||
UserId: th.BasicUser.Id,
|
||||
ChannelId: channelPrivate.Id,
|
||||
RootId: rootPostPrivateChannel.Id,
|
||||
Message: fmt.Sprintf("@%s", th.BasicUser2.Username),
|
||||
}, channelPrivate, false, true)
|
||||
|
||||
require.Nil(t, appErr)
|
||||
|
||||
// get top threads for team, as user 1 and user 2
|
||||
// user 1 should see both threads, while user 2 should see only thread in public channel.
|
||||
|
||||
topTeamThreadsByUser1, appErr := th.App.GetTopThreadsForTeamSince(th.Context, th.BasicTeam.Id, th.BasicUser.Id, &model.InsightsOpts{StartUnixMilli: 200, PerPage: 100})
|
||||
require.Nil(t, appErr)
|
||||
require.Len(t, topTeamThreadsByUser1.Items, 2)
|
||||
require.Equal(t, topTeamThreadsByUser1.Items[0].Post.Id, rootPostPrivateChannel.Id)
|
||||
require.Equal(t, topTeamThreadsByUser1.Items[1].Post.Id, rootPostPublicChannel.Id)
|
||||
|
||||
topTeamThreadsByUser2, appErr := th.App.GetTopThreadsForTeamSince(th.Context, th.BasicTeam.Id, th.BasicUser2.Id, &model.InsightsOpts{StartUnixMilli: 200, PerPage: 100})
|
||||
require.Nil(t, appErr)
|
||||
require.Len(t, topTeamThreadsByUser2.Items, 1)
|
||||
require.Equal(t, topTeamThreadsByUser2.Items[0].Post.Id, rootPostPublicChannel.Id)
|
||||
|
||||
// add user2 to private channel and it can see 2 top threads.
|
||||
th.AddUserToChannel(th.BasicUser2, channelPrivate)
|
||||
topTeamThreadsByUser2IncludingPrivate, appErr := th.App.GetTopThreadsForTeamSince(th.Context, th.BasicTeam.Id, th.BasicUser2.Id, &model.InsightsOpts{StartUnixMilli: 200, PerPage: 100})
|
||||
require.Nil(t, appErr)
|
||||
require.Len(t, topTeamThreadsByUser2IncludingPrivate.Items, 2)
|
||||
}
|
||||
func TestGetTopThreadsForUserSince(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.ConfigStore.SetReadOnlyFF(false)
|
||||
defer th.ConfigStore.SetReadOnlyFF(true)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
|
||||
// create a public channel, a private channel
|
||||
channelPublic := th.CreateChannel(th.Context, th.BasicTeam)
|
||||
channelPrivate := th.CreatePrivateChannel(th.Context, th.BasicTeam)
|
||||
th.AddUserToChannel(th.BasicUser, channelPublic)
|
||||
th.AddUserToChannel(th.BasicUser, channelPrivate)
|
||||
th.AddUserToChannel(th.BasicUser2, channelPublic)
|
||||
th.AddUserToChannel(th.BasicUser2, channelPrivate)
|
||||
|
||||
// create two threads: one in public channel, one in private
|
||||
// post in public channel has both users interacting, post in private only has user1 interacting
|
||||
|
||||
rootPostPublicChannel, appErr := th.App.CreatePost(th.Context, &model.Post{
|
||||
UserId: th.BasicUser.Id,
|
||||
ChannelId: channelPublic.Id,
|
||||
Message: "root post pub",
|
||||
}, channelPublic, false, true)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
_, appErr = th.App.CreatePost(th.Context, &model.Post{
|
||||
UserId: th.BasicUser2.Id,
|
||||
ChannelId: channelPublic.Id,
|
||||
RootId: rootPostPublicChannel.Id,
|
||||
Message: "reply post 1",
|
||||
}, channelPublic, false, true)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
rootPostPrivateChannel, appErr := th.App.CreatePost(th.Context, &model.Post{
|
||||
UserId: th.BasicUser.Id,
|
||||
ChannelId: channelPrivate.Id,
|
||||
Message: "root post priv",
|
||||
}, channelPrivate, false, true)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
_, appErr = th.App.CreatePost(th.Context, &model.Post{
|
||||
UserId: th.BasicUser.Id,
|
||||
ChannelId: channelPrivate.Id,
|
||||
RootId: rootPostPrivateChannel.Id,
|
||||
Message: "reply post 1",
|
||||
}, channelPrivate, false, true)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
_, appErr = th.App.CreatePost(th.Context, &model.Post{
|
||||
UserId: th.BasicUser.Id,
|
||||
ChannelId: channelPrivate.Id,
|
||||
RootId: rootPostPrivateChannel.Id,
|
||||
Message: "reply post 2",
|
||||
}, channelPrivate, false, true)
|
||||
|
||||
require.Nil(t, appErr)
|
||||
|
||||
// get top threads for user, as user 1 and user 2
|
||||
// user 1 should see both threads, while user 2 should see only thread in public channel
|
||||
// (even if user2 is in the private channel it hasn't interacted with the thread there.)
|
||||
|
||||
topUser1Threads, appErr := th.App.GetTopThreadsForUserSince(th.Context, th.BasicTeam.Id, th.BasicUser.Id, &model.InsightsOpts{StartUnixMilli: 200, PerPage: 100})
|
||||
require.Nil(t, appErr)
|
||||
require.Len(t, topUser1Threads.Items, 2)
|
||||
require.Equal(t, topUser1Threads.Items[0].Post.Id, rootPostPrivateChannel.Id)
|
||||
require.Equal(t, topUser1Threads.Items[0].ReplyCount, int64(2))
|
||||
require.Equal(t, topUser1Threads.Items[1].Post.Id, rootPostPublicChannel.Id)
|
||||
require.Contains(t, topUser1Threads.Items[1].Participants, th.BasicUser2.Id)
|
||||
require.Equal(t, topUser1Threads.Items[1].ReplyCount, int64(1))
|
||||
|
||||
topUser2Threads, appErr := th.App.GetTopThreadsForUserSince(th.Context, th.BasicTeam.Id, th.BasicUser2.Id, &model.InsightsOpts{StartUnixMilli: 200, PerPage: 100})
|
||||
require.Nil(t, appErr)
|
||||
require.Len(t, topUser2Threads.Items, 1)
|
||||
require.Equal(t, topUser2Threads.Items[0].Post.Id, rootPostPublicChannel.Id)
|
||||
require.Equal(t, topUser2Threads.Items[0].ReplyCount, int64(1))
|
||||
|
||||
// deleting the root post results in the thread not making it to top threads list
|
||||
_, appErr = th.App.DeletePost(th.Context, rootPostPublicChannel.Id, th.BasicUser.Id)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
topUser1ThreadsAfterPost1Delete, appErr := th.App.GetTopThreadsForUserSince(th.Context, th.BasicTeam.Id, th.BasicUser.Id, &model.InsightsOpts{StartUnixMilli: 200, PerPage: 100})
|
||||
require.Nil(t, appErr)
|
||||
require.Len(t, topUser1ThreadsAfterPost1Delete.Items, 1)
|
||||
|
||||
// reply with user2 in thread2. deleting that reply, shouldn't give any top thread for user2 if the user2 unsubscribes to the thread after deleting the comment
|
||||
replyPostUser2InPrivate, appErr := th.App.CreatePost(th.Context, &model.Post{
|
||||
UserId: th.BasicUser2.Id,
|
||||
ChannelId: channelPrivate.Id,
|
||||
RootId: rootPostPrivateChannel.Id,
|
||||
Message: "reply post 3",
|
||||
}, channelPrivate, false, true)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
topUser2ThreadsAfterPrivateReply, appErr := th.App.GetTopThreadsForUserSince(th.Context, th.BasicTeam.Id, th.BasicUser2.Id, &model.InsightsOpts{StartUnixMilli: 200, PerPage: 100})
|
||||
require.Nil(t, appErr)
|
||||
require.Len(t, topUser2ThreadsAfterPrivateReply.Items, 1)
|
||||
|
||||
// deleting reply, and unfollowing thread
|
||||
_, appErr = th.App.DeletePost(th.Context, replyPostUser2InPrivate.Id, th.BasicUser2.Id)
|
||||
require.Nil(t, appErr)
|
||||
// unfollow thread
|
||||
_, err := th.App.Srv().Store().Thread().MaintainMembership(th.BasicUser2.Id, rootPostPrivateChannel.Id, store.ThreadMembershipOpts{
|
||||
Following: false,
|
||||
UpdateFollowing: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
topUser2ThreadsAfterPrivateReplyDelete, appErr := th.App.GetTopThreadsForUserSince(th.Context, th.BasicTeam.Id, th.BasicUser2.Id, &model.InsightsOpts{StartUnixMilli: 200, PerPage: 100})
|
||||
require.Nil(t, appErr)
|
||||
require.Len(t, topUser2ThreadsAfterPrivateReplyDelete.Items, 0)
|
||||
}
|
||||
|
||||
func TestGetEditHistoryForPost(t *testing.T) {
|
||||
t.Skip("This needs fixing, OriginalId seems to be empty for all posts")
|
||||
th := Setup(t).InitBasic()
|
||||
@@ -3262,120 +3073,3 @@ func TestGetEditHistoryForPost(t *testing.T) {
|
||||
require.Empty(t, edits)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTopDMsForUserSince(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.ConfigStore.SetReadOnlyFF(false)
|
||||
defer th.ConfigStore.SetReadOnlyFF(true)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
|
||||
// users
|
||||
user := th.CreateUser()
|
||||
u1 := th.CreateUser()
|
||||
u2 := th.CreateUser()
|
||||
u3 := th.CreateUser()
|
||||
u4 := th.CreateUser()
|
||||
// user direct messages
|
||||
chUser1, nErr := th.App.createDirectChannel(th.Context, u1.Id, user.Id)
|
||||
fmt.Println(chUser1, nErr)
|
||||
require.Nil(t, nErr)
|
||||
chUser2, nErr := th.App.createDirectChannel(th.Context, u2.Id, user.Id)
|
||||
require.Nil(t, nErr)
|
||||
chUser3, nErr := th.App.createDirectChannel(th.Context, u3.Id, user.Id)
|
||||
require.Nil(t, nErr)
|
||||
// other user direct message
|
||||
chUser3User4, nErr := th.App.createDirectChannel(th.Context, u3.Id, u4.Id)
|
||||
require.Nil(t, nErr)
|
||||
|
||||
// sample post data
|
||||
// for u1
|
||||
_, err := th.App.CreatePostAsUser(th.Context, &model.Post{
|
||||
ChannelId: chUser1.Id,
|
||||
UserId: u1.Id,
|
||||
}, "", false)
|
||||
require.Nil(t, err)
|
||||
_, err = th.App.CreatePostAsUser(th.Context, &model.Post{
|
||||
ChannelId: chUser1.Id,
|
||||
UserId: user.Id,
|
||||
}, "", false)
|
||||
require.Nil(t, err)
|
||||
// for u2: 1 post
|
||||
_, err = th.App.CreatePostAsUser(th.Context, &model.Post{
|
||||
ChannelId: chUser2.Id,
|
||||
UserId: u2.Id,
|
||||
}, "", false)
|
||||
require.Nil(t, err)
|
||||
// for user-u3: 3 posts
|
||||
for i := 0; i < 3; i++ {
|
||||
_, err = th.App.CreatePostAsUser(th.Context, &model.Post{
|
||||
ChannelId: chUser3.Id,
|
||||
UserId: user.Id,
|
||||
}, "", false)
|
||||
require.Nil(t, err)
|
||||
}
|
||||
// for u4-u3: 4 posts
|
||||
_, err = th.App.CreatePostAsUser(th.Context, &model.Post{
|
||||
ChannelId: chUser3User4.Id,
|
||||
UserId: u3.Id,
|
||||
}, "", false)
|
||||
require.Nil(t, err)
|
||||
_, err = th.App.CreatePostAsUser(th.Context, &model.Post{
|
||||
ChannelId: chUser3User4.Id,
|
||||
UserId: u4.Id,
|
||||
}, "", false)
|
||||
require.Nil(t, err)
|
||||
_, err = th.App.CreatePostAsUser(th.Context, &model.Post{
|
||||
ChannelId: chUser3User4.Id,
|
||||
UserId: u3.Id,
|
||||
}, "", false)
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = th.App.CreatePostAsUser(th.Context, &model.Post{
|
||||
ChannelId: chUser3User4.Id,
|
||||
UserId: u4.Id,
|
||||
}, "", false)
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Run("should return topDMs when userid is specified ", func(t *testing.T) {
|
||||
topDMs, err := th.App.GetTopDMsForUserSince(user.Id, &model.InsightsOpts{StartUnixMilli: 100, Page: 0, PerPage: 100})
|
||||
require.Nil(t, err)
|
||||
// len of topDMs.Items should be 3
|
||||
require.Len(t, topDMs.Items, 3)
|
||||
// check order, magnitude of items
|
||||
// fmt.Println(topDMs.Items[0].MessageCount, topDMs.Items[1].MessageCount, topDMs.Items[2].MessageCount)
|
||||
require.Equal(t, topDMs.Items[0].SecondParticipant.Id, u3.Id)
|
||||
require.Equal(t, topDMs.Items[0].MessageCount, int64(3))
|
||||
require.Equal(t, topDMs.Items[1].SecondParticipant.Id, u1.Id)
|
||||
require.Equal(t, topDMs.Items[1].MessageCount, int64(2))
|
||||
require.Equal(t, topDMs.Items[2].SecondParticipant.Id, u2.Id)
|
||||
require.Equal(t, topDMs.Items[2].MessageCount, int64(1))
|
||||
// this also ensures that u3-u4 conversation doesn't show up in others' top DMs.
|
||||
})
|
||||
t.Run("topDMs should only consider user's DM channels ", func(t *testing.T) {
|
||||
// u4 only takes part in one conversation
|
||||
topDMs, err := th.App.GetTopDMsForUserSince(u4.Id, &model.InsightsOpts{StartUnixMilli: 100, Page: 0, PerPage: 100})
|
||||
require.Nil(t, err)
|
||||
// len of topDMs.Items should be 3
|
||||
require.Len(t, topDMs.Items, 1)
|
||||
// check order, magnitude of items
|
||||
require.Equal(t, topDMs.Items[0].SecondParticipant.Id, u3.Id)
|
||||
require.Equal(t, topDMs.Items[0].MessageCount, int64(4))
|
||||
})
|
||||
|
||||
t.Run("topDMs will not consider deleted second user", func(t *testing.T) {
|
||||
// u4 only takes part in one conversation
|
||||
topDMs, err := th.App.GetTopDMsForUserSince(u4.Id, &model.InsightsOpts{StartUnixMilli: 100, Page: 0, PerPage: 100})
|
||||
require.Nil(t, err)
|
||||
// len of topDMs.Items should be 1
|
||||
require.Len(t, topDMs.Items, 1)
|
||||
// delete user3
|
||||
err = th.App.PermanentDeleteUser(th.Context, u3)
|
||||
require.Nil(t, err)
|
||||
topDMs, err = th.App.GetTopDMsForUserSince(u4.Id, &model.InsightsOpts{StartUnixMilli: 100, Page: 0, PerPage: 100})
|
||||
require.Nil(t, err)
|
||||
// len of topDMs.Items should be 0 since u3 is deleted
|
||||
require.Len(t, topDMs.Items, 0)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -100,30 +100,6 @@ func populateEmptyReactions(postIDs []string, reactions map[string][]*model.Reac
|
||||
return reactions
|
||||
}
|
||||
|
||||
func (a *App) GetTopReactionsForTeamSince(teamID string, userID string, opts *model.InsightsOpts) (*model.TopReactionList, *model.AppError) {
|
||||
if !a.Config().FeatureFlags.InsightsEnabled {
|
||||
return nil, model.NewAppError("GetTopReactionsForTeamSince", "api.insights.feature_disabled", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
topReactionList, err := a.Srv().Store().Reaction().GetTopForTeamSince(teamID, userID, opts.StartUnixMilli, opts.Page*opts.PerPage, opts.PerPage)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetTopReactionsForTeamSince", model.NoTranslation, nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return topReactionList, nil
|
||||
}
|
||||
|
||||
func (a *App) GetTopReactionsForUserSince(userID string, teamID string, opts *model.InsightsOpts) (*model.TopReactionList, *model.AppError) {
|
||||
if !a.Config().FeatureFlags.InsightsEnabled {
|
||||
return nil, model.NewAppError("GetTopReactionsForUserSince", "api.insights.feature_disabled", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
topReactionList, err := a.Srv().Store().Reaction().GetTopForUserSince(userID, teamID, opts.StartUnixMilli, opts.Page*opts.PerPage, opts.PerPage)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetTopReactionsForUserSince", model.NoTranslation, nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return topReactionList, nil
|
||||
}
|
||||
|
||||
func (a *App) DeleteReactionForPost(c *request.Context, reaction *model.Reaction) *model.AppError {
|
||||
post, err := a.GetSinglePost(reaction.PostId, false)
|
||||
if err != nil {
|
||||
|
||||
@@ -5,7 +5,6 @@ package app
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -85,347 +84,3 @@ func TestSharedChannelSyncForReactionActions(t *testing.T) {
|
||||
assert.Equal(t, channel.Id, sharedChannelService.channelNotifications[1])
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTopReactionsForTeamSince(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.Server.platform.SetConfigReadOnlyFF(false)
|
||||
defer th.Server.platform.SetConfigReadOnlyFF(true)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
|
||||
userId := th.BasicUser.Id
|
||||
user2Id := th.BasicUser2.Id
|
||||
|
||||
post1 := th.CreatePost(th.BasicChannel)
|
||||
post2 := th.CreatePost(th.BasicChannel)
|
||||
post3 := th.CreatePost(th.BasicChannel)
|
||||
post4 := th.CreatePost(th.BasicChannel)
|
||||
post5 := th.CreatePost(th.BasicChannel)
|
||||
|
||||
userReactions := []*model.Reaction{
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post1.Id,
|
||||
EmojiName: "happy",
|
||||
},
|
||||
{
|
||||
UserId: user2Id,
|
||||
PostId: post1.Id,
|
||||
EmojiName: "happy",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post1.Id,
|
||||
EmojiName: "sad",
|
||||
},
|
||||
{
|
||||
UserId: user2Id,
|
||||
PostId: post1.Id,
|
||||
EmojiName: "sad",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post1.Id,
|
||||
EmojiName: "smile",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post1.Id,
|
||||
EmojiName: "joy",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post1.Id,
|
||||
EmojiName: "100",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post2.Id,
|
||||
EmojiName: "sad",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post2.Id,
|
||||
EmojiName: "smile",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post2.Id,
|
||||
EmojiName: "joy",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post2.Id,
|
||||
EmojiName: "100",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post3.Id,
|
||||
EmojiName: "smile",
|
||||
},
|
||||
{
|
||||
UserId: user2Id,
|
||||
PostId: post3.Id,
|
||||
EmojiName: "smile",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post3.Id,
|
||||
EmojiName: "joy",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post3.Id,
|
||||
EmojiName: "100",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post4.Id,
|
||||
EmojiName: "joy",
|
||||
},
|
||||
{
|
||||
UserId: user2Id,
|
||||
PostId: post4.Id,
|
||||
EmojiName: "joy",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post4.Id,
|
||||
EmojiName: "100",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post5.Id,
|
||||
EmojiName: "100",
|
||||
},
|
||||
{
|
||||
UserId: user2Id,
|
||||
PostId: post5.Id,
|
||||
EmojiName: "100",
|
||||
},
|
||||
{
|
||||
UserId: user2Id,
|
||||
PostId: post5.Id,
|
||||
EmojiName: "+1",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post1.Id,
|
||||
EmojiName: "100",
|
||||
CreateAt: model.GetMillisForTime(time.Now().Add(time.Hour * time.Duration(-25))),
|
||||
},
|
||||
}
|
||||
|
||||
for _, userReaction := range userReactions {
|
||||
_, err := th.App.Srv().Store().Reaction().Save(userReaction)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
teamId := th.BasicChannel.TeamId
|
||||
|
||||
var expectedTopReactions [5]*model.TopReaction
|
||||
expectedTopReactions[0] = &model.TopReaction{EmojiName: "100", Count: int64(6)}
|
||||
expectedTopReactions[1] = &model.TopReaction{EmojiName: "joy", Count: int64(5)}
|
||||
expectedTopReactions[2] = &model.TopReaction{EmojiName: "smile", Count: int64(4)}
|
||||
expectedTopReactions[3] = &model.TopReaction{EmojiName: "sad", Count: int64(3)}
|
||||
expectedTopReactions[4] = &model.TopReaction{EmojiName: "happy", Count: int64(2)}
|
||||
|
||||
timeRange, _ := model.GetStartOfDayForTimeRange(model.TimeRangeToday, time.Now().Location())
|
||||
|
||||
t.Run("get-top-reactions-for-team-since", func(t *testing.T) {
|
||||
topReactions, err := th.App.GetTopReactionsForTeamSince(teamId, userId, &model.InsightsOpts{StartUnixMilli: timeRange.UnixMilli(), Page: 0, PerPage: 5})
|
||||
require.Nil(t, err)
|
||||
reactions := topReactions.Items
|
||||
|
||||
for i, reaction := range reactions {
|
||||
assert.Equal(t, expectedTopReactions[i].EmojiName, reaction.EmojiName)
|
||||
assert.Equal(t, expectedTopReactions[i].Count, reaction.Count)
|
||||
}
|
||||
topReactions, err = th.App.GetTopReactionsForTeamSince(teamId, userId, &model.InsightsOpts{StartUnixMilli: timeRange.UnixMilli(), Page: 1, PerPage: 5})
|
||||
require.Nil(t, err)
|
||||
reactions = topReactions.Items
|
||||
|
||||
assert.Equal(t, "+1", reactions[0].EmojiName)
|
||||
assert.Equal(t, int64(1), reactions[0].Count)
|
||||
})
|
||||
|
||||
t.Run("get-top-reactions-for-team-since feature flag", func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = false })
|
||||
defer th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
_, err := th.App.GetTopReactionsForTeamSince(userId, teamId, &model.InsightsOpts{StartUnixMilli: timeRange.UnixMilli(), Page: 0, PerPage: 5})
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTopReactionsForUserSince(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.Server.platform.SetConfigReadOnlyFF(false)
|
||||
defer th.Server.platform.SetConfigReadOnlyFF(true)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
|
||||
userId := th.BasicUser.Id
|
||||
|
||||
post1 := th.CreatePost(th.BasicChannel)
|
||||
post2 := th.CreatePost(th.BasicChannel)
|
||||
post3 := th.CreatePost(th.BasicChannel)
|
||||
post4 := th.CreatePost(th.BasicChannel)
|
||||
post5 := th.CreatePost(th.BasicChannel)
|
||||
post6 := th.CreatePost(th.BasicChannel)
|
||||
|
||||
userReactions := []*model.Reaction{
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post1.Id,
|
||||
EmojiName: "happy",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post2.Id,
|
||||
EmojiName: "happy",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post3.Id,
|
||||
EmojiName: "happy",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post4.Id,
|
||||
EmojiName: "happy",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post5.Id,
|
||||
EmojiName: "happy",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post6.Id,
|
||||
EmojiName: "happy",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post1.Id,
|
||||
EmojiName: "smile",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post2.Id,
|
||||
EmojiName: "smile",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post3.Id,
|
||||
EmojiName: "smile",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post4.Id,
|
||||
EmojiName: "smile",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post5.Id,
|
||||
EmojiName: "smile",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post1.Id,
|
||||
EmojiName: "+1",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post2.Id,
|
||||
EmojiName: "+1",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post3.Id,
|
||||
EmojiName: "+1",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post4.Id,
|
||||
EmojiName: "+1",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post1.Id,
|
||||
EmojiName: "heart",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post2.Id,
|
||||
EmojiName: "heart",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post3.Id,
|
||||
EmojiName: "heart",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post1.Id,
|
||||
EmojiName: "blush",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post2.Id,
|
||||
EmojiName: "blush",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post1.Id,
|
||||
EmojiName: "100",
|
||||
},
|
||||
{
|
||||
UserId: userId,
|
||||
PostId: post1.Id,
|
||||
EmojiName: "100",
|
||||
CreateAt: model.GetMillisForTime(time.Now().Add(time.Hour * time.Duration(-25))),
|
||||
},
|
||||
}
|
||||
|
||||
for _, userReaction := range userReactions {
|
||||
_, err := th.App.Srv().Store().Reaction().Save(userReaction)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
teamId := th.BasicChannel.TeamId
|
||||
|
||||
var expectedTopReactions [5]*model.TopReaction
|
||||
expectedTopReactions[0] = &model.TopReaction{EmojiName: "happy", Count: int64(6)}
|
||||
expectedTopReactions[1] = &model.TopReaction{EmojiName: "smile", Count: int64(5)}
|
||||
expectedTopReactions[2] = &model.TopReaction{EmojiName: "+1", Count: int64(4)}
|
||||
expectedTopReactions[3] = &model.TopReaction{EmojiName: "heart", Count: int64(3)}
|
||||
expectedTopReactions[4] = &model.TopReaction{EmojiName: "blush", Count: int64(2)}
|
||||
|
||||
timeRange, _ := model.GetStartOfDayForTimeRange(model.TimeRangeToday, time.Now().Location())
|
||||
|
||||
t.Run("get-top-reactions-for-user-since", func(t *testing.T) {
|
||||
topReactions, err := th.App.GetTopReactionsForUserSince(userId, teamId, &model.InsightsOpts{StartUnixMilli: timeRange.UnixMilli(), Page: 0, PerPage: 5})
|
||||
require.Nil(t, err)
|
||||
reactions := topReactions.Items
|
||||
|
||||
for i, reaction := range reactions {
|
||||
assert.Equal(t, expectedTopReactions[i].EmojiName, reaction.EmojiName)
|
||||
assert.Equal(t, expectedTopReactions[i].Count, reaction.Count)
|
||||
}
|
||||
|
||||
topReactions, err = th.App.GetTopReactionsForUserSince(userId, teamId, &model.InsightsOpts{StartUnixMilli: timeRange.UnixMilli(), Page: 1, PerPage: 5})
|
||||
require.Nil(t, err)
|
||||
reactions = topReactions.Items
|
||||
assert.Equal(t, "100", reactions[0].EmojiName)
|
||||
assert.Equal(t, int64(1), reactions[0].Count)
|
||||
})
|
||||
|
||||
t.Run("get-top-reactions-for-user-since feature flag", func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = false })
|
||||
defer th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
_, err := th.App.GetTopReactionsForUserSince(userId, teamId, &model.InsightsOpts{StartUnixMilli: timeRange.UnixMilli(), Page: 0, PerPage: 5})
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2116,17 +2116,3 @@ func (a *App) ClearTeamMembersCache(teamID string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) GetNewTeamMembersSince(c request.CTX, teamID string, opts *model.InsightsOpts) (*model.NewTeamMembersList, int64, *model.AppError) {
|
||||
if !a.Config().FeatureFlags.InsightsEnabled {
|
||||
return nil, 0, model.NewAppError("GetNewTeamMembersSince", "app.insights.feature_disabled", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
showFullName := *a.Config().PrivacySettings.ShowFullName || a.SessionHasPermissionTo(*c.Session(), model.PermissionManageSystem)
|
||||
ntms, count, err := a.Srv().Store().Team().GetNewTeamMembersSince(teamID, opts.StartUnixMilli, opts.Page*opts.PerPage, opts.PerPage, showFullName)
|
||||
if err != nil {
|
||||
return nil, 0, model.NewAppError("GetNewTeamMembersSince", model.NoTranslation, nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
return ntms, count, nil
|
||||
}
|
||||
|
||||
@@ -1620,200 +1620,6 @@ func TestInviteGuestsToChannelsGracefully(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetNewTeamMembersSince(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.ConfigStore.SetReadOnlyFF(false)
|
||||
defer th.ConfigStore.SetReadOnlyFF(true)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
|
||||
team := th.CreateTeam()
|
||||
|
||||
t.Run("counts team members", func(t *testing.T) {
|
||||
var originalExpectedCount int64
|
||||
var newTeamMemberJoinTime int64
|
||||
var anotherUser *model.User
|
||||
|
||||
t.Run("since time 0", func(t *testing.T) {
|
||||
teamMembers, err := th.App.Srv().Store().Team().GetMembers(team.Id, 0, 1000, nil)
|
||||
require.NoError(t, err)
|
||||
originalExpectedCount = int64(len(teamMembers))
|
||||
_, actualCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, originalExpectedCount, actualCount)
|
||||
})
|
||||
|
||||
t.Run("after a new team member was added", func(t *testing.T) {
|
||||
anotherUser = th.CreateUser()
|
||||
newTeamMember, appErr := th.App.JoinUserToTeam(th.Context, team, anotherUser, "")
|
||||
newTeamMemberJoinTime = newTeamMember.CreateAt
|
||||
require.Nil(t, appErr)
|
||||
_, actualCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, originalExpectedCount+1, actualCount)
|
||||
})
|
||||
|
||||
t.Run("after a team member was added to a different team, ensuring the wrong team's member count isn't incremented", func(t *testing.T) {
|
||||
anotherUser2 := th.CreateUser()
|
||||
anotherTeam := th.CreateTeam()
|
||||
_, appErr := th.App.JoinUserToTeam(th.Context, anotherTeam, anotherUser2, "")
|
||||
require.Nil(t, appErr)
|
||||
_, actualCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, originalExpectedCount+1, actualCount)
|
||||
})
|
||||
|
||||
t.Run("since a given time", func(t *testing.T) {
|
||||
_, actualCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: newTeamMemberJoinTime, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, int64(1), actualCount)
|
||||
})
|
||||
|
||||
t.Run("after a team member was removed", func(t *testing.T) {
|
||||
th.RemoveUserFromTeam(anotherUser, team)
|
||||
_, actualCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, originalExpectedCount, actualCount)
|
||||
})
|
||||
|
||||
t.Run("after a user was deactivated", func(t *testing.T) {
|
||||
_, appErr := th.App.JoinUserToTeam(th.Context, team, anotherUser, "")
|
||||
require.Nil(t, appErr)
|
||||
_, beforeCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
_, appErr = th.App.UpdateActive(th.Context, anotherUser, false)
|
||||
defer th.App.UpdateActive(th.Context, anotherUser, true)
|
||||
require.Nil(t, appErr)
|
||||
_, afterCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, beforeCount-1, afterCount)
|
||||
})
|
||||
|
||||
t.Run("after a user was permanently deleted", func(t *testing.T) {
|
||||
_, beforeCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
appErr = th.App.PermanentDeleteUser(th.Context, anotherUser)
|
||||
require.Nil(t, appErr)
|
||||
_, afterCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, beforeCount-1, afterCount)
|
||||
})
|
||||
|
||||
t.Run("exclude bots", func(t *testing.T) {
|
||||
user := th.CreateUser()
|
||||
_, appErr := th.App.ConvertUserToBot(user)
|
||||
require.Nil(t, appErr)
|
||||
_, appErr = th.App.JoinUserToTeam(th.Context, team, user, "")
|
||||
require.Nil(t, appErr)
|
||||
_, actualCount, appErr := th.App.GetNewTeamMembersSince(th.Context, team.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, originalExpectedCount, actualCount)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("returns the correct team members", func(t *testing.T) {
|
||||
var originalExpectedMembers []*model.TeamMember
|
||||
var newTeamMemberJoinTime int64
|
||||
var anotherUser *model.User
|
||||
|
||||
uIDs := func(members []*model.TeamMember) []string {
|
||||
ids := []string{}
|
||||
for _, member := range members {
|
||||
ids = append(ids, member.UserId)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
nUIDs := func(members []*model.NewTeamMember) []string {
|
||||
ids := []string{}
|
||||
for _, member := range members {
|
||||
ids = append(ids, member.Id)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
t.Run("since time 0", func(t *testing.T) {
|
||||
var err error
|
||||
originalExpectedMembers, err = th.App.Srv().Store().Team().GetMembers(th.BasicTeam.Id, 0, 1000, nil)
|
||||
require.NoError(t, err)
|
||||
actualMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.ElementsMatch(t, uIDs(originalExpectedMembers), nUIDs(actualMembersList.Items))
|
||||
})
|
||||
|
||||
t.Run("after a new team member was added", func(t *testing.T) {
|
||||
anotherUser = th.CreateUser()
|
||||
newTeamMember, appErr := th.App.JoinUserToTeam(th.Context, th.BasicTeam, anotherUser, "")
|
||||
newTeamMemberJoinTime = newTeamMember.CreateAt
|
||||
require.Nil(t, appErr)
|
||||
actualMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.ElementsMatch(t, append(uIDs(originalExpectedMembers), anotherUser.Id), nUIDs(actualMembersList.Items))
|
||||
})
|
||||
|
||||
t.Run("after a team member was added to a different team, ensuring the wrong team's member count isn't incremented", func(t *testing.T) {
|
||||
anotherUser2 := th.CreateUser()
|
||||
anotherTeam := th.CreateTeam()
|
||||
_, appErr := th.App.JoinUserToTeam(th.Context, anotherTeam, anotherUser2, "")
|
||||
require.Nil(t, appErr)
|
||||
actualMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.ElementsMatch(t, append(uIDs(originalExpectedMembers), anotherUser.Id), nUIDs(actualMembersList.Items))
|
||||
})
|
||||
|
||||
t.Run("since a given time", func(t *testing.T) {
|
||||
actualMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: newTeamMemberJoinTime, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Len(t, actualMembersList.Items, 1)
|
||||
require.Equal(t, anotherUser.Id, actualMembersList.Items[0].Id)
|
||||
})
|
||||
|
||||
t.Run("after a team member was removed", func(t *testing.T) {
|
||||
th.RemoveUserFromTeam(anotherUser, th.BasicTeam)
|
||||
actualMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.ElementsMatch(t, uIDs(originalExpectedMembers), nUIDs(actualMembersList.Items))
|
||||
})
|
||||
|
||||
t.Run("after a user was deactivated", func(t *testing.T) {
|
||||
_, appErr := th.App.JoinUserToTeam(th.Context, th.BasicTeam, anotherUser, "")
|
||||
require.Nil(t, appErr)
|
||||
beforeMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Contains(t, nUIDs(beforeMembersList.Items), anotherUser.Id)
|
||||
_, appErr = th.App.UpdateActive(th.Context, anotherUser, false)
|
||||
defer th.App.UpdateActive(th.Context, anotherUser, true)
|
||||
require.Nil(t, appErr)
|
||||
afterMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.NotContains(t, nUIDs(afterMembersList.Items), anotherUser.Id)
|
||||
})
|
||||
|
||||
t.Run("after a user was permanently deleted", func(t *testing.T) {
|
||||
beforeMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.Contains(t, nUIDs(beforeMembersList.Items), anotherUser.Id)
|
||||
appErr = th.App.PermanentDeleteUser(th.Context, anotherUser)
|
||||
require.Nil(t, appErr)
|
||||
afterMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.NotContains(t, nUIDs(afterMembersList.Items), anotherUser.Id)
|
||||
})
|
||||
|
||||
t.Run("exclude bots", func(t *testing.T) {
|
||||
user := th.CreateUser()
|
||||
_, appErr := th.App.ConvertUserToBot(user)
|
||||
require.Nil(t, appErr)
|
||||
_, appErr = th.App.JoinUserToTeam(th.Context, th.BasicTeam, user, "")
|
||||
require.Nil(t, appErr)
|
||||
actualMembersList, _, appErr := th.App.GetNewTeamMembersSince(th.Context, th.BasicTeam.Id, &model.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 1000})
|
||||
require.Nil(t, appErr)
|
||||
require.ElementsMatch(t, uIDs(originalExpectedMembers), nUIDs(actualMembersList.Items))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTeamSendEvents(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -284,14 +284,6 @@ func (a *App) createUserOrGuest(c request.CTX, user *model.User, guest bool) (*m
|
||||
tutorialStepPref := model.Preference{UserId: ruser.Id, Category: model.PreferenceCategoryTutorialSteps, Name: ruser.Id, Value: "0"}
|
||||
|
||||
preferences := model.Preferences{recommendedNextStepsPref, tutorialStepPref}
|
||||
|
||||
if a.Config().FeatureFlags.InsightsEnabled {
|
||||
// We don't want to show the insights intro modal for new users
|
||||
preferences = append(preferences, model.Preference{UserId: ruser.Id, Category: model.PreferenceCategoryInsights, Name: model.PreferenceNameInsights, Value: "{\"insights_modal_viewed\":true}"})
|
||||
} else {
|
||||
preferences = append(preferences, model.Preference{UserId: ruser.Id, Category: model.PreferenceCategoryInsights, Name: model.PreferenceNameInsights, Value: "{\"insights_modal_viewed\":false}"})
|
||||
}
|
||||
|
||||
if err := a.Srv().Store().Preference().Save(preferences); err != nil {
|
||||
c.Logger().Warn("Encountered error saving user preferences", mlog.Err(err))
|
||||
}
|
||||
|
||||
@@ -1810,16 +1810,10 @@ func TestCreateUserWithInitialPreferences(t *testing.T) {
|
||||
t.Run("successfully create a user with initial tutorial and recommended steps preferences", func(t *testing.T) {
|
||||
th.ConfigStore.SetReadOnlyFF(false)
|
||||
defer th.ConfigStore.SetReadOnlyFF(true)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
|
||||
testUser := th.CreateUser()
|
||||
defer th.App.PermanentDeleteUser(th.Context, testUser)
|
||||
|
||||
insightsPref, appErr := th.App.GetPreferenceByCategoryAndNameForUser(testUser.Id, model.PreferenceCategoryInsights, model.PreferenceNameInsights)
|
||||
require.Nil(t, appErr)
|
||||
assert.Equal(t, "insights_tutorial_state", insightsPref.Name)
|
||||
assert.Equal(t, "{\"insights_modal_viewed\":true}", insightsPref.Value)
|
||||
|
||||
tutorialStepPref, appErr := th.App.GetPreferenceByCategoryAndNameForUser(testUser.Id, model.PreferenceCategoryTutorialSteps, testUser.Id)
|
||||
require.Nil(t, appErr)
|
||||
assert.Equal(t, testUser.Id, tutorialStepPref.Name)
|
||||
@@ -1831,38 +1825,12 @@ func TestCreateUserWithInitialPreferences(t *testing.T) {
|
||||
assert.Equal(t, "false", recommendedNextStepsPref[0].Value)
|
||||
})
|
||||
|
||||
t.Run("successfully create a user with insights feature flag disabled", func(t *testing.T) {
|
||||
t.Run("successfully create a guest user with initial tutorial and recommended steps preferences", func(t *testing.T) {
|
||||
th.Server.platform.SetConfigReadOnlyFF(false)
|
||||
defer th.Server.platform.SetConfigReadOnlyFF(true)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = false })
|
||||
defer th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
testUser := th.CreateUser()
|
||||
defer th.App.PermanentDeleteUser(th.Context, testUser)
|
||||
|
||||
insightsPref, appErr := th.App.GetPreferenceByCategoryAndNameForUser(testUser.Id, model.PreferenceCategoryInsights, model.PreferenceNameInsights)
|
||||
require.Nil(t, appErr)
|
||||
assert.Equal(t, "insights_tutorial_state", insightsPref.Name)
|
||||
assert.Equal(t, "{\"insights_modal_viewed\":false}", insightsPref.Value)
|
||||
|
||||
recommendedNextStepsPref, appErr := th.App.GetPreferenceByCategoryForUser(testUser.Id, model.PreferenceRecommendedNextSteps)
|
||||
require.Nil(t, appErr)
|
||||
assert.Equal(t, model.PreferenceRecommendedNextSteps, recommendedNextStepsPref[0].Category)
|
||||
assert.Equal(t, "hide", recommendedNextStepsPref[0].Name)
|
||||
assert.Equal(t, "false", recommendedNextStepsPref[0].Value)
|
||||
})
|
||||
|
||||
t.Run("successfully create a guest user with initial tutorial, insights and recommended steps preferences", func(t *testing.T) {
|
||||
th.Server.platform.SetConfigReadOnlyFF(false)
|
||||
defer th.Server.platform.SetConfigReadOnlyFF(true)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
testUser := th.CreateGuest()
|
||||
defer th.App.PermanentDeleteUser(th.Context, testUser)
|
||||
|
||||
insightsPref, appErr := th.App.GetPreferenceByCategoryAndNameForUser(testUser.Id, model.PreferenceCategoryInsights, model.PreferenceNameInsights)
|
||||
require.Nil(t, appErr)
|
||||
assert.Equal(t, "insights_tutorial_state", insightsPref.Name)
|
||||
assert.Equal(t, "{\"insights_modal_viewed\":true}", insightsPref.Value)
|
||||
|
||||
tutorialStepPref, appErr := th.App.GetPreferenceByCategoryAndNameForUser(testUser.Id, model.PreferenceCategoryTutorialSteps, testUser.Id)
|
||||
require.Nil(t, appErr)
|
||||
assert.Equal(t, testUser.Id, tutorialStepPref.Name)
|
||||
|
||||
Ссылка в новой задаче
Block a user