[MM-42739] Insights - Top Channels API Endpoint (#19953)

* [MM-42739] Initial setup for top channels for team

* [MM-42739] Add initial tests

* [MM-42739] Update tests

* [MM-42739] Add top channels for user

* [MM-42739] Fix query

* [MM-42739] Update query

* [MM-42739] Improve query performance

* [MM-42739] Remove rank

* [MM-42739] Fix tests to use new time range today

* [MM-42739] Add tests for top channels for user

* [MM-42739] Add test for pagination

* Remove top channels by time struct

* [MM-42739] Update test names

* [MM-42739] Remove rank from top reactions

* [MM-42739] Return empty array instead of nil when result is empty

* [MM-42739] Add additional tests and update permissions check for teams

* [MM-42739] Add excluded channel tests for top reactions

* [MM-42739] Move insights to api4/insights and keep time range as string until required

* [MM-42739] Update queries only check DeleteAt after union

* [MM-42739] Improve query performance by using publicchannels table

* [MM-42739] Fix broken query after merge
Этот коммит содержится в:
Mylon Suren
2022-04-26 14:42:24 -04:00
коммит произвёл GitHub
родитель 9d37a6cc3d
Коммит 52ac449012
22 изменённых файлов: 1453 добавлений и 510 удалений

Просмотреть файл

@@ -754,6 +754,8 @@ 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(teamID, userID string, opts *model.InsightsOpts) (*model.TopChannelList, *model.AppError)
GetTopChannelsForUserSince(userID, teamID string, opts *model.InsightsOpts) (*model.TopChannelList, *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)
GetUploadSession(uploadId string) (*model.UploadSession, *model.AppError)

Просмотреть файл

@@ -3439,3 +3439,27 @@ func (s *Server) getDirectChannel(userID, otherUserID string) (*model.Channel, *
return channel, nil
}
func (a *App) GetTopChannelsForTeamSince(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", "app.channel.get_top_for_team_since.app_error", nil, err.Error(), http.StatusInternalServerError)
}
return topChannels, nil
}
func (a *App) GetTopChannelsForUserSince(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", "app.channel.get_top_for_user_since.app_error", nil, err.Error(), http.StatusInternalServerError)
}
return topChannels, nil
}

Просмотреть файл

@@ -2446,3 +2446,119 @@ func TestMarkUnreadWithThreads(t *testing.T) {
})
})
}
func TestGetTopChannelsForTeamSince(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
th.Server.configStore.SetReadOnlyFF(false)
defer th.Server.configStore.SetReadOnlyFF(true)
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
channel2 := th.CreateChannel(th.BasicTeam)
channel3 := th.CreatePrivateChannel(th.BasicTeam)
channel4 := th.CreatePrivateChannel(th.BasicTeam)
channel5 := th.CreateChannel(th.BasicTeam)
channel6 := th.CreatePrivateChannel(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.GetStartUnixMilliForTimeRange(model.TimeRangeToday)
t.Run("get-top-channels-for-team-since", func(t *testing.T) {
topChannels, err := th.App.GetTopChannelsForTeamSince(th.BasicChannel.TeamId, th.BasicUser.Id, &model.InsightsOpts{StartUnixMilli: timeRange, 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.BasicChannel.TeamId, th.BasicUser.Id, &model.InsightsOpts{StartUnixMilli: timeRange, 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.Server.configStore.SetReadOnlyFF(false)
defer th.Server.configStore.SetReadOnlyFF(true)
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
channel2 := th.CreateChannel(th.BasicTeam)
channel3 := th.CreatePrivateChannel(th.BasicTeam)
channel4 := th.CreatePrivateChannel(th.BasicTeam)
channel5 := th.CreateChannel(th.BasicTeam)
channel6 := th.CreatePrivateChannel(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.GetStartUnixMilliForTimeRange(model.TimeRangeToday)
t.Run("get-top-channels-for-user-since", func(t *testing.T) {
topChannels, err := th.App.GetTopChannelsForUserSince(th.BasicUser.Id, "", &model.InsightsOpts{StartUnixMilli: timeRange, 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.BasicUser.Id, th.BasicChannel.TeamId, &model.InsightsOpts{StartUnixMilli: timeRange, 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)
})
}

Просмотреть файл

@@ -9596,6 +9596,50 @@ func (a *OpenTracingAppLayer) GetTokenById(token string) (*model.Token, *model.A
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetTopChannelsForTeamSince(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(teamID, userID, opts)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetTopChannelsForUserSince(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(userID, teamID, 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")