[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
Этот коммит содержится в:
@@ -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")
|
||||
|
||||
Ссылка в новой задаче
Block a user