mirror of
https://github.com/mattermost/mattermost.git
synced 2026-08-27 05:37:15 -05:00
[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
This commit is contained in:
@@ -134,6 +134,9 @@ type Routes struct {
|
||||
SharedChannels *mux.Router // 'api/v4/sharedchannels'
|
||||
|
||||
Permissions *mux.Router // 'api/v4/permissions'
|
||||
|
||||
InsightsForTeam *mux.Router // 'api/v4/teams/{team_id:[A-Za-z0-9]+}/top'
|
||||
InsightsForUser *mux.Router // 'api/v4/users/me/top'
|
||||
}
|
||||
|
||||
type API struct {
|
||||
@@ -255,6 +258,9 @@ func Init(srv *app.Server) (*API, error) {
|
||||
|
||||
api.BaseRoutes.Permissions = api.BaseRoutes.APIRoot.PathPrefix("/permissions").Subrouter()
|
||||
|
||||
api.BaseRoutes.InsightsForTeam = api.BaseRoutes.Team.PathPrefix("/top").Subrouter()
|
||||
api.BaseRoutes.InsightsForUser = api.BaseRoutes.Users.PathPrefix("/me/top").Subrouter()
|
||||
|
||||
api.InitUser()
|
||||
api.InitBot()
|
||||
api.InitTeam()
|
||||
@@ -296,6 +302,7 @@ func Init(srv *app.Server) (*API, error) {
|
||||
api.InitSharedChannels()
|
||||
api.InitPermissions()
|
||||
api.InitExport()
|
||||
api.InitInsights()
|
||||
if err := api.InitGraphQL(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package api4
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
)
|
||||
|
||||
func (api *API) InitInsights() {
|
||||
// Reactions
|
||||
api.BaseRoutes.InsightsForTeam.Handle("/reactions", api.APISessionRequired(getTopReactionsForTeamSince)).Methods("GET")
|
||||
api.BaseRoutes.InsightsForUser.Handle("/reactions", api.APISessionRequired(getTopReactionsForUserSince)).Methods("GET")
|
||||
|
||||
// Channels
|
||||
api.BaseRoutes.InsightsForTeam.Handle("/channels", api.APISessionRequired(getTopChannelsForTeamSince)).Methods("GET")
|
||||
api.BaseRoutes.InsightsForUser.Handle("/channels", api.APISessionRequired(getTopChannelsForUserSince)).Methods("GET")
|
||||
}
|
||||
|
||||
// Top Reactions
|
||||
|
||||
func getTopReactionsForTeamSince(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireTeamId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
team, err := c.App.GetTeam(c.Params.TeamId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), team.Id, model.PermissionViewTeam) {
|
||||
c.SetPermissionError(model.PermissionViewTeam)
|
||||
return
|
||||
}
|
||||
|
||||
startTime, err := model.GetStartUnixMilliForTimeRange(c.Params.TimeRange)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
topReactionList, err := c.App.GetTopReactionsForTeamSince(c.Params.TeamId, c.AppContext.Session().UserId, &model.InsightsOpts{
|
||||
StartUnixMilli: startTime,
|
||||
Page: c.Params.Page,
|
||||
PerPage: c.Params.PerPage,
|
||||
})
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
js, jsonErr := json.Marshal(topReactionList)
|
||||
if jsonErr != nil {
|
||||
c.Err = model.NewAppError("getTopReactionsForTeamSince", "api.marshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(js)
|
||||
}
|
||||
|
||||
func getTopReactionsForUserSince(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.Params.TeamId = r.URL.Query().Get("team_id")
|
||||
|
||||
// TeamId is an optional parameter
|
||||
if c.Params.TeamId != "" {
|
||||
if !model.IsValidId(c.Params.TeamId) {
|
||||
c.SetInvalidURLParam("team_id")
|
||||
return
|
||||
}
|
||||
|
||||
team, teamErr := c.App.GetTeam(c.Params.TeamId)
|
||||
if teamErr != nil {
|
||||
c.Err = teamErr
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), team.Id, model.PermissionViewTeam) {
|
||||
c.SetPermissionError(model.PermissionViewTeam)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
startTime, err := model.GetStartUnixMilliForTimeRange(c.Params.TimeRange)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
topReactionList, err := c.App.GetTopReactionsForUserSince(c.AppContext.Session().UserId, c.Params.TeamId, &model.InsightsOpts{
|
||||
StartUnixMilli: startTime,
|
||||
Page: c.Params.Page,
|
||||
PerPage: c.Params.PerPage,
|
||||
})
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
js, jsonErr := json.Marshal(topReactionList)
|
||||
if jsonErr != nil {
|
||||
c.Err = model.NewAppError("getTopReactionsForUserSince", "api.marshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(js)
|
||||
}
|
||||
|
||||
// Top Channels
|
||||
|
||||
func getTopChannelsForTeamSince(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireTeamId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
team, err := c.App.GetTeam(c.Params.TeamId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), team.Id, model.PermissionViewTeam) {
|
||||
c.SetPermissionError(model.PermissionViewTeam)
|
||||
return
|
||||
}
|
||||
|
||||
startTime, err := model.GetStartUnixMilliForTimeRange(c.Params.TimeRange)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
topChannels, err := c.App.GetTopChannelsForTeamSince(c.Params.TeamId, c.AppContext.Session().UserId, &model.InsightsOpts{
|
||||
StartUnixMilli: startTime,
|
||||
Page: c.Params.Page,
|
||||
PerPage: c.Params.PerPage,
|
||||
})
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
js, jsonErr := json.Marshal(topChannels)
|
||||
if jsonErr != nil {
|
||||
c.Err = model.NewAppError("getTopChannelsForTeamSince", "api.marshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(js)
|
||||
}
|
||||
|
||||
func getTopChannelsForUserSince(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.Params.TeamId = r.URL.Query().Get("team_id")
|
||||
|
||||
// TeamId is an optional parameter
|
||||
if c.Params.TeamId != "" {
|
||||
if !model.IsValidId(c.Params.TeamId) {
|
||||
c.SetInvalidURLParam("team_id")
|
||||
return
|
||||
}
|
||||
|
||||
team, teamErr := c.App.GetTeam(c.Params.TeamId)
|
||||
if teamErr != nil {
|
||||
c.Err = teamErr
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), team.Id, model.PermissionViewTeam) {
|
||||
c.SetPermissionError(model.PermissionViewTeam)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
startTime, err := model.GetStartUnixMilliForTimeRange(c.Params.TimeRange)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
topChannels, err := c.App.GetTopChannelsForUserSince(c.AppContext.Session().UserId, c.Params.TeamId, &model.InsightsOpts{
|
||||
StartUnixMilli: startTime,
|
||||
Page: c.Params.Page,
|
||||
PerPage: c.Params.PerPage,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
js, jsonErr := json.Marshal(topChannels)
|
||||
if jsonErr != nil {
|
||||
c.Err = model.NewAppError("getTopChannelsForUserSince", "api.marshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(js)
|
||||
}
|
||||
@@ -0,0 +1,600 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package api4
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Top Reactions
|
||||
|
||||
func TestGetTopReactionsForTeamSince(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 })
|
||||
|
||||
client := th.Client
|
||||
|
||||
userId := th.BasicUser.Id
|
||||
user2Id := th.BasicUser2.Id
|
||||
|
||||
post1 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post2 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post3 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post4 := &model.Post{UserId: user2Id, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post5 := &model.Post{UserId: user2Id, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
|
||||
post1, _, _ = client.CreatePost(post1)
|
||||
post2, _, _ = client.CreatePost(post2)
|
||||
post3, _, _ = client.CreatePost(post3)
|
||||
post4, _, _ = client.CreatePost(post4)
|
||||
post5, _, _ = client.CreatePost(post5)
|
||||
|
||||
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)}
|
||||
|
||||
t.Run("get-top-reactions-for-team-since", func(t *testing.T) {
|
||||
topReactions, _, err := client.GetTopReactionsForTeamSince(teamId, model.TimeRangeToday, 0, 5)
|
||||
require.NoError(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 = client.GetTopReactionsForTeamSince(teamId, model.TimeRangeToday, 1, 5)
|
||||
require.NoError(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 exclude channels user is not member of", func(t *testing.T) {
|
||||
excludedChannel := th.CreatePrivateChannel()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
post, _, err := client.CreatePost(&model.Post{UserId: userId, ChannelId: excludedChannel.Id, Message: "zz" + model.NewId() + "a"})
|
||||
require.NoError(t, err)
|
||||
|
||||
reaction := &model.Reaction{
|
||||
UserId: userId,
|
||||
PostId: post.Id,
|
||||
EmojiName: "confused",
|
||||
}
|
||||
|
||||
_, err = th.App.Srv().Store.Reaction().Save(reaction)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
th.RemoveUserFromChannel(th.BasicUser, excludedChannel)
|
||||
|
||||
topReactions, _, err := client.GetTopReactionsForTeamSince(teamId, model.TimeRangeToday, 0, 5)
|
||||
require.NoError(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 = client.GetTopReactionsForTeamSince(teamId, model.TimeRangeToday, 1, 5)
|
||||
require.NoError(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 invalid team id", func(t *testing.T) {
|
||||
_, resp, err := client.GetTopReactionsForTeamSince("12345", model.TimeRangeToday, 0, 5)
|
||||
assert.Error(t, err)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
_, resp, err = client.GetTopReactionsForTeamSince(model.NewId(), model.TimeRangeToday, 0, 5)
|
||||
assert.Error(t, err)
|
||||
CheckNotFoundStatus(t, resp)
|
||||
})
|
||||
|
||||
t.Run("get-top-reactions-for-team-since not a member of team", func(t *testing.T) {
|
||||
th.UnlinkUserFromTeam(th.BasicUser, th.BasicTeam)
|
||||
_, resp, err := client.GetTopReactionsForTeamSince(teamId, model.TimeRangeToday, 0, 5)
|
||||
assert.Error(t, err)
|
||||
CheckForbiddenStatus(t, resp)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTopReactionsForUserSince(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 })
|
||||
|
||||
client := th.Client
|
||||
|
||||
userId := th.BasicUser.Id
|
||||
|
||||
post1 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post2 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post3 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post4 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post5 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post6 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
|
||||
post1, _, _ = client.CreatePost(post1)
|
||||
post2, _, _ = client.CreatePost(post2)
|
||||
post3, _, _ = client.CreatePost(post3)
|
||||
post4, _, _ = client.CreatePost(post4)
|
||||
post5, _, _ = client.CreatePost(post5)
|
||||
post6, _, _ = client.CreatePost(post6)
|
||||
|
||||
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)}
|
||||
|
||||
t.Run("get-top-reactions-for-user-since", func(t *testing.T) {
|
||||
topReactions, _, err := client.GetTopReactionsForUserSince(teamId, model.TimeRangeToday, 0, 5)
|
||||
require.NoError(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 = client.GetTopReactionsForUserSince(teamId, model.TimeRangeToday, 1, 5)
|
||||
require.NoError(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 invalid team id", func(t *testing.T) {
|
||||
_, resp, err := client.GetTopReactionsForUserSince("invalid_team_id", model.TimeRangeToday, 0, 5)
|
||||
assert.Error(t, err)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
_, resp, err = client.GetTopReactionsForUserSince(model.NewId(), model.TimeRangeToday, 0, 5)
|
||||
assert.Error(t, err)
|
||||
CheckNotFoundStatus(t, resp)
|
||||
})
|
||||
|
||||
t.Run("get-top-reactions-for-user-since not a member of team", func(t *testing.T) {
|
||||
th.UnlinkUserFromTeam(th.BasicUser, th.BasicTeam)
|
||||
_, resp, err := client.GetTopReactionsForUserSince(teamId, model.TimeRangeToday, 0, 5)
|
||||
assert.Error(t, err)
|
||||
CheckForbiddenStatus(t, resp)
|
||||
})
|
||||
}
|
||||
|
||||
// Top Channels
|
||||
|
||||
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 })
|
||||
|
||||
client := th.Client
|
||||
userId := th.BasicUser.Id
|
||||
|
||||
channel4 := th.CreatePublicChannel()
|
||||
channel5 := th.CreatePrivateChannel()
|
||||
channel6 := th.CreatePrivateChannel()
|
||||
th.App.AddUserToChannel(th.BasicUser, channel4, false)
|
||||
th.App.AddUserToChannel(th.BasicUser, channel5, false)
|
||||
th.App.AddUserToChannel(th.BasicUser, channel6, false)
|
||||
|
||||
channelIDs := [6]string{th.BasicChannel.Id, th.BasicChannel2.Id, th.BasicPrivateChannel.Id, channel4.Id, channel5.Id, channel6.Id}
|
||||
|
||||
i := len(channelIDs)
|
||||
for _, channelID := range channelIDs {
|
||||
for j := i; j > 0; j-- {
|
||||
_, _, err := client.CreatePost(&model.Post{UserId: userId, ChannelId: channelID, Message: "zz" + model.NewId() + "a"})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
i--
|
||||
}
|
||||
|
||||
teamId := th.BasicChannel.TeamId
|
||||
|
||||
expectedTopChannels := []struct {
|
||||
ID string
|
||||
MessageCount int64
|
||||
}{
|
||||
{ID: th.BasicChannel.Id, MessageCount: 7},
|
||||
{ID: th.BasicChannel2.Id, MessageCount: 5},
|
||||
{ID: th.BasicPrivateChannel.Id, MessageCount: 4},
|
||||
{ID: channel4.Id, MessageCount: 3},
|
||||
{ID: channel5.Id, MessageCount: 2},
|
||||
}
|
||||
|
||||
t.Run("get-top-channels-for-team-since", func(t *testing.T) {
|
||||
topChannels, _, err := client.GetTopChannelsForTeamSince(teamId, model.TimeRangeToday, 0, 5)
|
||||
require.NoError(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 = client.GetTopChannelsForTeamSince(teamId, model.TimeRangeToday, 1, 5)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, channel6.Id, topChannels.Items[0].ID)
|
||||
assert.Equal(t, int64(1), topChannels.Items[0].MessageCount)
|
||||
})
|
||||
|
||||
t.Run("get-top-channels-for-user-since exclude channels user is not member of", func(t *testing.T) {
|
||||
excludedChannel := th.CreatePrivateChannel()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
_, _, err := client.CreatePost(&model.Post{UserId: userId, ChannelId: excludedChannel.Id, Message: "zz" + model.NewId() + "a"})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
th.RemoveUserFromChannel(th.BasicUser, excludedChannel)
|
||||
|
||||
topChannels, _, err := client.GetTopChannelsForTeamSince(teamId, model.TimeRangeToday, 0, 5)
|
||||
require.NoError(t, err)
|
||||
|
||||
for i, channel := range topChannels.Items {
|
||||
assert.Equal(t, expectedTopChannels[i].ID, channel.ID)
|
||||
assert.Equal(t, expectedTopChannels[i].MessageCount, channel.MessageCount)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("get-top-channels-for-team-since invalid team id", func(t *testing.T) {
|
||||
_, resp, err := client.GetTopChannelsForTeamSince("12345", model.TimeRangeToday, 0, 5)
|
||||
assert.Error(t, err)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
_, resp, err = client.GetTopChannelsForTeamSince(model.NewId(), model.TimeRangeToday, 0, 5)
|
||||
assert.Error(t, err)
|
||||
CheckNotFoundStatus(t, resp)
|
||||
})
|
||||
|
||||
t.Run("get-top-channels-for-team-since not a member of team", func(t *testing.T) {
|
||||
th.UnlinkUserFromTeam(th.BasicUser, th.BasicTeam)
|
||||
_, resp, err := client.GetTopChannelsForTeamSince(teamId, model.TimeRangeToday, 0, 5)
|
||||
assert.Error(t, err)
|
||||
CheckForbiddenStatus(t, resp)
|
||||
})
|
||||
}
|
||||
|
||||
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 })
|
||||
|
||||
client := th.Client
|
||||
userId := th.BasicUser.Id
|
||||
|
||||
channel4 := th.CreatePublicChannel()
|
||||
channel5 := th.CreatePrivateChannel()
|
||||
channel6 := th.CreatePrivateChannel()
|
||||
th.App.AddUserToChannel(th.BasicUser, channel4, false)
|
||||
th.App.AddUserToChannel(th.BasicUser, channel5, false)
|
||||
th.App.AddUserToChannel(th.BasicUser, channel6, false)
|
||||
|
||||
channelIDs := [6]string{th.BasicChannel.Id, th.BasicChannel2.Id, th.BasicPrivateChannel.Id, channel4.Id, channel5.Id, channel6.Id}
|
||||
|
||||
i := len(channelIDs)
|
||||
for _, channelID := range channelIDs {
|
||||
for j := i; j > 0; j-- {
|
||||
_, _, err := client.CreatePost(&model.Post{UserId: userId, ChannelId: channelID, Message: "zz" + model.NewId() + "a"})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
i--
|
||||
}
|
||||
|
||||
teamId := th.BasicChannel.TeamId
|
||||
|
||||
expectedTopChannels := []struct {
|
||||
ID string
|
||||
MessageCount int64
|
||||
}{
|
||||
{ID: th.BasicChannel.Id, MessageCount: 6},
|
||||
{ID: th.BasicChannel2.Id, MessageCount: 5},
|
||||
{ID: th.BasicPrivateChannel.Id, MessageCount: 4},
|
||||
{ID: channel4.Id, MessageCount: 3},
|
||||
{ID: channel5.Id, MessageCount: 2},
|
||||
}
|
||||
|
||||
t.Run("get-top-channels-for-user-since", func(t *testing.T) {
|
||||
topChannels, _, err := client.GetTopChannelsForUserSince(teamId, model.TimeRangeToday, 0, 5)
|
||||
require.NoError(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 = client.GetTopChannelsForUserSince("", model.TimeRangeToday, 1, 5)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, channel6.Id, topChannels.Items[0].ID)
|
||||
assert.Equal(t, int64(1), topChannels.Items[0].MessageCount)
|
||||
})
|
||||
|
||||
t.Run("get-top-channels-for-user-since invalid team id", func(t *testing.T) {
|
||||
_, resp, err := client.GetTopChannelsForUserSince("12345", model.TimeRangeToday, 0, 5)
|
||||
assert.Error(t, err)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
_, resp, err = client.GetTopChannelsForUserSince(model.NewId(), model.TimeRangeToday, 0, 5)
|
||||
assert.Error(t, err)
|
||||
CheckNotFoundStatus(t, resp)
|
||||
})
|
||||
|
||||
t.Run("get-top-channels-for-user-since not a member of team", func(t *testing.T) {
|
||||
th.UnlinkUserFromTeam(th.BasicUser, th.BasicTeam)
|
||||
_, resp, err := client.GetTopChannelsForUserSince(teamId, model.TimeRangeToday, 0, 5)
|
||||
assert.Error(t, err)
|
||||
CheckForbiddenStatus(t, resp)
|
||||
})
|
||||
}
|
||||
@@ -16,9 +16,6 @@ func (api *API) InitReaction() {
|
||||
api.BaseRoutes.Post.Handle("/reactions", api.APISessionRequired(getReactions)).Methods("GET")
|
||||
api.BaseRoutes.ReactionByNameForPostForUser.Handle("", api.APISessionRequired(deleteReaction)).Methods("DELETE")
|
||||
api.BaseRoutes.Posts.Handle("/ids/reactions", api.APISessionRequired(getBulkReactions)).Methods("POST")
|
||||
|
||||
api.BaseRoutes.Team.Handle("/top/reactions", api.APISessionRequired(getTopReactionsForTeamSince)).Methods("GET")
|
||||
api.BaseRoutes.Users.Handle("/me/top/reactions", api.APISessionRequired(getTopReactionsForUserSince)).Methods("GET")
|
||||
}
|
||||
|
||||
func saveReaction(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
@@ -141,85 +138,3 @@ func getBulkReactions(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
w.Write(js)
|
||||
}
|
||||
|
||||
func getTopReactionsForTeamSince(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireTeamId().RequireTimeRange()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
team, err := c.App.GetTeam(c.Params.TeamId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if (!team.AllowOpenInvite || team.Type != model.TeamOpen) && !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), team.Id, model.PermissionViewTeam) {
|
||||
c.SetPermissionError(model.PermissionViewTeam)
|
||||
return
|
||||
}
|
||||
|
||||
topReactionList, err := c.App.GetTopReactionsForTeamSince(c.Params.TeamId, c.AppContext.Session().UserId, &model.InsightsOpts{
|
||||
StartUnixMilli: c.Params.TimeRange,
|
||||
Page: c.Params.Page,
|
||||
PerPage: c.Params.PerPage,
|
||||
})
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
js, jsonErr := json.Marshal(topReactionList)
|
||||
if jsonErr != nil {
|
||||
c.Err = model.NewAppError("getTopReactionsForTeamSince", "api.marshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(js)
|
||||
}
|
||||
|
||||
func getTopReactionsForUserSince(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireTimeRange()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.Params.TeamId = r.URL.Query().Get("team_id")
|
||||
|
||||
// TeamId is an optional parameter
|
||||
if c.Params.TeamId != "" {
|
||||
if !model.IsValidId(c.Params.TeamId) {
|
||||
c.SetInvalidURLParam("team_id")
|
||||
return
|
||||
}
|
||||
|
||||
team, teamErr := c.App.GetTeam(c.Params.TeamId)
|
||||
if teamErr != nil {
|
||||
c.Err = teamErr
|
||||
return
|
||||
}
|
||||
|
||||
if (!team.AllowOpenInvite || team.Type != model.TeamOpen) && !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), team.Id, model.PermissionViewTeam) {
|
||||
c.SetPermissionError(model.PermissionViewTeam)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
topReactionList, err := c.App.GetTopReactionsForUserSince(c.AppContext.Session().UserId, c.Params.TeamId, &model.InsightsOpts{
|
||||
StartUnixMilli: c.Params.TimeRange,
|
||||
Page: c.Params.Page,
|
||||
PerPage: c.Params.PerPage,
|
||||
})
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
js, jsonErr := json.Marshal(topReactionList)
|
||||
if jsonErr != nil {
|
||||
c.Err = model.NewAppError("getTopReactionsForUserSince", "api.marshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(js)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ package api4
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -585,367 +584,3 @@ func TestGetBulkReactions(t *testing.T) {
|
||||
CheckUnauthorizedStatus(t, resp)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTopReactionsForTeamSince(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 })
|
||||
|
||||
client := th.Client
|
||||
|
||||
userId := th.BasicUser.Id
|
||||
user2Id := th.BasicUser2.Id
|
||||
|
||||
post1 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post2 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post3 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post4 := &model.Post{UserId: user2Id, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post5 := &model.Post{UserId: user2Id, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
|
||||
post1, _, _ = client.CreatePost(post1)
|
||||
post2, _, _ = client.CreatePost(post2)
|
||||
post3, _, _ = client.CreatePost(post3)
|
||||
post4, _, _ = client.CreatePost(post4)
|
||||
post5, _, _ = client.CreatePost(post5)
|
||||
|
||||
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)}
|
||||
|
||||
t.Run("get-top-reactions-for-team-since", func(t *testing.T) {
|
||||
topReactions, _, err := client.GetTopReactionsForTeamSince(teamId, model.TimeRangeToday, 0, 5)
|
||||
require.NoError(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 = client.GetTopReactionsForTeamSince(teamId, model.TimeRangeToday, 1, 5)
|
||||
require.NoError(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 invalid team id", func(t *testing.T) {
|
||||
_, resp, err := client.GetTopReactionsForTeamSince("12345", model.TimeRangeToday, 0, 5)
|
||||
assert.Error(t, err)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
_, resp, err = client.GetTopReactionsForTeamSince(model.NewId(), model.TimeRangeToday, 0, 5)
|
||||
assert.Error(t, err)
|
||||
CheckNotFoundStatus(t, resp)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTopReactionsForUserSince(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 })
|
||||
|
||||
client := th.Client
|
||||
|
||||
userId := th.BasicUser.Id
|
||||
|
||||
post1 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post2 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post3 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post4 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post5 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
post6 := &model.Post{UserId: userId, ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a"}
|
||||
|
||||
post1, _, _ = client.CreatePost(post1)
|
||||
post2, _, _ = client.CreatePost(post2)
|
||||
post3, _, _ = client.CreatePost(post3)
|
||||
post4, _, _ = client.CreatePost(post4)
|
||||
post5, _, _ = client.CreatePost(post5)
|
||||
post6, _, _ = client.CreatePost(post6)
|
||||
|
||||
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)}
|
||||
|
||||
t.Run("get-top-reactions-for-user-since", func(t *testing.T) {
|
||||
topReactions, _, err := client.GetTopReactionsForUserSince(teamId, model.TimeRangeToday, 0, 5)
|
||||
require.NoError(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 = client.GetTopReactionsForUserSince(teamId, model.TimeRangeToday, 1, 5)
|
||||
require.NoError(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 invalid team id", func(t *testing.T) {
|
||||
_, resp, err := client.GetTopReactionsForUserSince("invalid_team_id", model.TimeRangeToday, 0, 5)
|
||||
assert.Error(t, err)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
_, resp, err = client.GetTopReactionsForUserSince(model.NewId(), model.TimeRangeToday, 0, 5)
|
||||
assert.Error(t, err)
|
||||
CheckNotFoundStatus(t, resp)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -4499,6 +4499,14 @@
|
||||
"id": "app.channel.get_public_channels.get.app_error",
|
||||
"translation": "Unable to get public channels."
|
||||
},
|
||||
{
|
||||
"id": "app.channel.get_top_for_team_since.app_error",
|
||||
"translation": " "
|
||||
},
|
||||
{
|
||||
"id": "app.channel.get_top_for_user_since.app_error",
|
||||
"translation": " "
|
||||
},
|
||||
{
|
||||
"id": "app.channel.get_unread.app_error",
|
||||
"translation": "Unable to get the channel unread messages."
|
||||
|
||||
@@ -3603,6 +3603,41 @@ func (c *Client4) AutocompleteChannelsForTeamForSearch(teamId, name string) (Cha
|
||||
return ch, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
// GetTopChannelsForTeamSince will return an ordered list of the top channels in a given team.
|
||||
func (c *Client4) GetTopChannelsForTeamSince(teamId string, timeRange string, page int, perPage int) (*TopChannelList, *Response, error) {
|
||||
query := fmt.Sprintf("?time_range=%v&page=%v&per_page=%v", timeRange, page, perPage)
|
||||
r, err := c.DoAPIGet(c.teamRoute(teamId)+"/top/channels"+query, "")
|
||||
if err != nil {
|
||||
return nil, BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
var topChannels *TopChannelList
|
||||
if jsonErr := json.NewDecoder(r.Body).Decode(&topChannels); jsonErr != nil {
|
||||
return nil, nil, NewAppError("GetTopChannelsForTeamSince", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return topChannels, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
// GetTopChannelsForUserSince will return an ordered list of your top channels in a given team.
|
||||
func (c *Client4) GetTopChannelsForUserSince(teamId string, timeRange string, page int, perPage int) (*TopChannelList, *Response, error) {
|
||||
query := fmt.Sprintf("?time_range=%v&page=%v&per_page=%v", timeRange, page, perPage)
|
||||
|
||||
if teamId != "" {
|
||||
query += fmt.Sprintf("&team_id=%v", teamId)
|
||||
}
|
||||
|
||||
r, err := c.DoAPIGet(c.usersRoute()+"/me/top/channels"+query, "")
|
||||
if err != nil {
|
||||
return nil, BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
var topChannels *TopChannelList
|
||||
if jsonErr := json.NewDecoder(r.Body).Decode(&topChannels); jsonErr != nil {
|
||||
return nil, nil, NewAppError("GetTopChannelsForUserSince", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return topChannels, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
// Post Section
|
||||
|
||||
// CreatePost creates a post based on the provided post struct.
|
||||
|
||||
+32
-12
@@ -24,21 +24,32 @@ type InsightsListData struct {
|
||||
HasNext bool `json:"has_next"`
|
||||
}
|
||||
|
||||
type InsightsData struct {
|
||||
Rank int `json:"rank"`
|
||||
}
|
||||
|
||||
// Top Reactions
|
||||
type TopReactionList struct {
|
||||
InsightsListData
|
||||
Items []*TopReaction `json:"items"`
|
||||
}
|
||||
|
||||
type TopReaction struct {
|
||||
InsightsData
|
||||
EmojiName string `json:"emoji_name"`
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// Top Channels
|
||||
type TopChannelList struct {
|
||||
InsightsListData
|
||||
Items []*TopChannel `json:"items"`
|
||||
}
|
||||
|
||||
type TopChannel struct {
|
||||
ID string `json:"id"`
|
||||
Type ChannelType `json:"type"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Name string `json:"name"`
|
||||
TeamID string `json:"team_id"`
|
||||
MessageCount int64 `json:"message_count"`
|
||||
}
|
||||
|
||||
// GetStartUnixMilliForTimeRange gets the unix start time in milliseconds from the given time range.
|
||||
// Time range can be one of: "1_day", "7_day", or "28_day".
|
||||
func GetStartUnixMilliForTimeRange(timeRange string) (int64, *AppError) {
|
||||
@@ -56,10 +67,10 @@ func GetStartUnixMilliForTimeRange(timeRange string) (int64, *AppError) {
|
||||
return GetStartOfDayMillis(now, offset), NewAppError("Insights.IsValidRequest", "model.insights.time_range.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// GetTopReactionListWithRankAndPagination adds a rank to each item in the given list of TopReaction and checks if there is
|
||||
// GetTopReactionListWithPagination adds a rank to each item in the given list of TopReaction and checks if there is
|
||||
// another page that can be fetched based on the given limit and offset. The given list of TopReaction is assumed to be
|
||||
// sorted by Count. Returns a TopReactionList.
|
||||
func GetTopReactionListWithRankAndPagination(reactions []*TopReaction, limit int, offset int) *TopReactionList {
|
||||
func GetTopReactionListWithPagination(reactions []*TopReaction, limit int) *TopReactionList {
|
||||
// Add pagination support
|
||||
var hasNext bool
|
||||
if (limit != 0) && (len(reactions) == limit+1) {
|
||||
@@ -67,10 +78,19 @@ func GetTopReactionListWithRankAndPagination(reactions []*TopReaction, limit int
|
||||
reactions = reactions[:len(reactions)-1]
|
||||
}
|
||||
|
||||
// Assign rank to each reaction
|
||||
for i, reaction := range reactions {
|
||||
reaction.Rank = offset + i + 1
|
||||
}
|
||||
|
||||
return &TopReactionList{InsightsListData: InsightsListData{HasNext: hasNext}, Items: reactions}
|
||||
}
|
||||
|
||||
// GetTopChannelListWithPagination adds a rank to each item in the given list of TopChannel and checks if there is
|
||||
// another page that can be fetched based on the given limit and offset. The given list of TopChannel is assumed to be
|
||||
// sorted by Score. Returns a TopChannelList.
|
||||
func GetTopChannelListWithPagination(channels []*TopChannel, limit int) *TopChannelList {
|
||||
// Add pagination support
|
||||
var hasNext bool
|
||||
if (limit != 0) && (len(channels) == limit+1) {
|
||||
hasNext = true
|
||||
channels = channels[:len(channels)-1]
|
||||
}
|
||||
|
||||
return &TopChannelList{InsightsListData: InsightsListData{HasNext: hasNext}, Items: channels}
|
||||
}
|
||||
|
||||
+39
-16
@@ -29,8 +29,7 @@ func TestGetStartUnixMilliForTimeRang(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTopReactionListWithRankAndPagination(t *testing.T) {
|
||||
|
||||
func TestGetTopReactionListWithPagination(t *testing.T) {
|
||||
reactions := []*TopReaction{
|
||||
{EmojiName: "smile", Count: 200},
|
||||
{EmojiName: "+1", Count: 190},
|
||||
@@ -61,21 +60,45 @@ func TestGetTopReactionListWithRankAndPagination(t *testing.T) {
|
||||
|
||||
for _, test := range hasNextTC {
|
||||
t.Run(test.Description, func(t *testing.T) {
|
||||
actual := GetTopReactionListWithRankAndPagination(reactions, test.Limit, test.Offset)
|
||||
actual := GetTopReactionListWithPagination(reactions, test.Limit)
|
||||
assert.Equal(t, test.Expected.HasNext, actual.HasNext)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTopChannelListWithPagination(t *testing.T) {
|
||||
channels := []*TopChannel{
|
||||
{ID: NewId(), MessageCount: 200},
|
||||
{ID: NewId(), MessageCount: 150},
|
||||
{ID: NewId(), MessageCount: 120},
|
||||
{ID: NewId(), MessageCount: 105},
|
||||
{ID: NewId(), MessageCount: 5},
|
||||
{ID: NewId(), MessageCount: 2}}
|
||||
|
||||
hasNextTC := []struct {
|
||||
Description string
|
||||
Limit int
|
||||
Offset int
|
||||
Expected *TopChannelList
|
||||
}{
|
||||
{
|
||||
Description: "has one page",
|
||||
Limit: len(channels),
|
||||
Offset: 0,
|
||||
Expected: &TopChannelList{InsightsListData: InsightsListData{HasNext: false}, Items: channels},
|
||||
},
|
||||
{
|
||||
Description: "has more than one page",
|
||||
Limit: len(channels) - 1,
|
||||
Offset: 0,
|
||||
Expected: &TopChannelList{InsightsListData: InsightsListData{HasNext: true}, Items: channels},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range hasNextTC {
|
||||
t.Run(test.Description, func(t *testing.T) {
|
||||
actual := GetTopChannelListWithPagination(channels, test.Limit)
|
||||
assert.Equal(t, test.Expected.HasNext, actual.HasNext)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("ranks for first and second page", func(t *testing.T) {
|
||||
firstPage := GetTopReactionListWithRankAndPagination(reactions, 5, 0)
|
||||
|
||||
for i, r := range firstPage.Items {
|
||||
assert.Equal(t, i+1, r.Rank)
|
||||
}
|
||||
|
||||
secondPage := GetTopReactionListWithRankAndPagination(reactions, 5, 5)
|
||||
for i, r := range secondPage.Items {
|
||||
assert.Equal(t, i+1+5, r.Rank)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1748,6 +1748,42 @@ func (s *OpenTracingLayerChannelStore) GetTeamMembersForChannel(channelID string
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerChannelStore) GetTopChannelsForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopChannelList, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetTopChannelsForTeamSince")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
result, err := s.ChannelStore.GetTopChannelsForTeamSince(teamID, userID, since, offset, limit)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerChannelStore) GetTopChannelsForUserSince(userID string, teamID string, since int64, offset int, limit int) (*model.TopChannelList, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetTopChannelsForUserSince")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
result, err := s.ChannelStore.GetTopChannelsForUserSince(userID, teamID, since, offset, limit)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerChannelStore) GroupSyncedChannelCount() (int64, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GroupSyncedChannelCount")
|
||||
|
||||
@@ -1975,6 +1975,48 @@ func (s *RetryLayerChannelStore) GetTeamMembersForChannel(channelID string) ([]s
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerChannelStore) GetTopChannelsForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopChannelList, error) {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
result, err := s.ChannelStore.GetTopChannelsForTeamSince(teamID, userID, since, offset, limit)
|
||||
if err == nil {
|
||||
return result, nil
|
||||
}
|
||||
if !isRepeatableError(err) {
|
||||
return result, err
|
||||
}
|
||||
tries++
|
||||
if tries >= 3 {
|
||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||
return result, err
|
||||
}
|
||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerChannelStore) GetTopChannelsForUserSince(userID string, teamID string, since int64, offset int, limit int) (*model.TopChannelList, error) {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
result, err := s.ChannelStore.GetTopChannelsForUserSince(userID, teamID, since, offset, limit)
|
||||
if err == nil {
|
||||
return result, nil
|
||||
}
|
||||
if !isRepeatableError(err) {
|
||||
return result, err
|
||||
}
|
||||
tries++
|
||||
if tries >= 3 {
|
||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||
return result, err
|
||||
}
|
||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerChannelStore) GroupSyncedChannelCount() (int64, error) {
|
||||
|
||||
tries := 0
|
||||
|
||||
@@ -4097,3 +4097,161 @@ func (s SqlChannelStore) GetTeamForChannel(channelID string) (*model.Team, error
|
||||
}
|
||||
return &team, nil
|
||||
}
|
||||
|
||||
// GetTopChannelsForTeamSince returns the filtered post counts of the following Channels sets:
|
||||
// a) those that are private channels in the given user's membership graph on the given team, and
|
||||
// b) those that are public channels in the given team.
|
||||
func (s SqlChannelStore) GetTopChannelsForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopChannelList, error) {
|
||||
channels := make([]*model.TopChannel, 0)
|
||||
var args []interface{}
|
||||
postgresPropQuery := `AND (Posts.Props ->> 'from_bot' IS NULL OR Posts.Props ->> 'from_bot' = 'false')`
|
||||
mySqlPropsQuery := `AND (JSON_EXTRACT(Posts.Props, '$.from_bot') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_bot') = 'false')`
|
||||
|
||||
query := `
|
||||
SELECT
|
||||
ID,
|
||||
Type,
|
||||
DisplayName,
|
||||
Name,
|
||||
TeamID,
|
||||
MessageCount
|
||||
FROM
|
||||
((SELECT
|
||||
Posts.ChannelId AS ID,
|
||||
'O' AS Type,
|
||||
PublicChannels.DisplayName AS DisplayName,
|
||||
PublicChannels.Name AS Name,
|
||||
PublicChannels.TeamId AS TeamID,
|
||||
count(Posts.Id) AS MessageCount,
|
||||
PublicChannels.DeleteAt AS DeleteAt
|
||||
FROM
|
||||
Posts
|
||||
LEFT JOIN PublicChannels on Posts.ChannelId = PublicChannels.Id
|
||||
WHERE
|
||||
Posts.DeleteAt = 0
|
||||
AND Posts.CreateAt > ?
|
||||
AND Posts.Type = ''`
|
||||
args = []interface{}{since}
|
||||
|
||||
if s.DriverName() == model.DatabaseDriverMysql {
|
||||
query += mySqlPropsQuery
|
||||
} else if s.DriverName() == model.DatabaseDriverPostgres {
|
||||
query += postgresPropQuery
|
||||
}
|
||||
|
||||
query += `
|
||||
AND PublicChannels.TeamId = ?
|
||||
GROUP BY
|
||||
Posts.ChannelId,
|
||||
PublicChannels.DisplayName,
|
||||
PublicChannels.Name,
|
||||
PublicChannels.TeamId,
|
||||
PublicChannels.DeleteAt)
|
||||
UNION ALL
|
||||
(SELECT
|
||||
Posts.ChannelId AS ID,
|
||||
Channels.Type AS Type,
|
||||
Channels.DisplayName AS DisplayName,
|
||||
Channels.Name AS Name,
|
||||
Channels.TeamId AS TeamID,
|
||||
count(Posts.Id) AS MessageCount,
|
||||
Channels.DeleteAt AS DeleteAt
|
||||
FROM
|
||||
Posts
|
||||
LEFT JOIN Channels on Posts.ChannelId = Channels.Id
|
||||
LEFT JOIN ChannelMembers on Posts.ChannelId = ChannelMembers.ChannelId
|
||||
WHERE
|
||||
Posts.DeleteAt = 0
|
||||
AND Posts.CreateAt > ?
|
||||
AND Posts.Type = ''`
|
||||
args = append(args, teamID, since)
|
||||
|
||||
if s.DriverName() == model.DatabaseDriverMysql {
|
||||
query += mySqlPropsQuery
|
||||
} else if s.DriverName() == model.DatabaseDriverPostgres {
|
||||
query += postgresPropQuery
|
||||
}
|
||||
|
||||
query += `
|
||||
AND Channels.TeamId = ?
|
||||
AND Channels.Type = 'P'
|
||||
AND ChannelMembers.UserId = ?
|
||||
GROUP BY
|
||||
Posts.ChannelId,
|
||||
Channels.Type,
|
||||
Channels.DisplayName,
|
||||
Channels.Name,
|
||||
Channels.TeamId,
|
||||
Channels.DeleteAt)) AS A
|
||||
WHERE
|
||||
DeleteAt = 0
|
||||
ORDER BY
|
||||
MessageCount DESC,
|
||||
Name ASC
|
||||
LIMIT ?
|
||||
OFFSET ?`
|
||||
args = append(args, teamID, userID, limit+1, offset)
|
||||
|
||||
if err := s.GetReplicaX().Select(&channels, query, args...); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get top Channels")
|
||||
}
|
||||
|
||||
return model.GetTopChannelListWithPagination(channels, limit), nil
|
||||
}
|
||||
|
||||
// GetTopChannelsForUserSince returns the filtered post counts of channels with with posts created by the user
|
||||
// after the given timestamp within the given team (or across the workspace if no team is given). Excludes DM and GM channels.
|
||||
func (s SqlChannelStore) GetTopChannelsForUserSince(userID string, teamID string, since int64, offset int, limit int) (*model.TopChannelList, error) {
|
||||
channels := make([]*model.TopChannel, 0)
|
||||
var args []interface{}
|
||||
var query string
|
||||
|
||||
query = `
|
||||
SELECT
|
||||
Posts.ChannelId AS ID,
|
||||
Channels.Type AS Type,
|
||||
Channels.DisplayName AS DisplayName,
|
||||
Channels.Name AS Name,
|
||||
Channels.TeamId AS TeamID,
|
||||
count(Posts.Id) AS MessageCount
|
||||
FROM
|
||||
Posts
|
||||
LEFT JOIN Channels on Posts.ChannelId = Channels.Id
|
||||
LEFT JOIN ChannelMembers on Posts.ChannelId = ChannelMembers.ChannelId
|
||||
WHERE
|
||||
Posts.DeleteAt = 0
|
||||
AND Posts.CreateAt > ?
|
||||
AND Posts.Type = ''
|
||||
AND Posts.UserID = ?
|
||||
AND Channels.DeleteAt = 0
|
||||
AND (Channels.Type = 'O' OR Channels.Type = 'P')
|
||||
AND ChannelMembers.UserId = ?`
|
||||
|
||||
args = []interface{}{since, userID, userID}
|
||||
|
||||
if teamID != "" {
|
||||
query += `
|
||||
AND Channels.TeamID = ?`
|
||||
args = append(args, teamID)
|
||||
}
|
||||
|
||||
query += `
|
||||
Group By
|
||||
Posts.ChannelId,
|
||||
Channels.Type,
|
||||
Channels.DisplayName,
|
||||
Channels.Name,
|
||||
Channels.TeamId
|
||||
ORDER BY
|
||||
MessageCount DESC,
|
||||
Name ASC
|
||||
LIMIT ?
|
||||
OFFSET ?`
|
||||
args = append(args, limit+1, offset)
|
||||
|
||||
if err := s.GetReplicaX().Select(&channels, query, args...); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get top Channels")
|
||||
}
|
||||
|
||||
return model.GetTopChannelListWithPagination(channels, limit), nil
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ func (s *SqlReactionStore) PermanentDeleteBatch(endTime int64, limit int64) (int
|
||||
// a) those created by anyone in private channels in the given user's membership graph on the given team, and
|
||||
// b) those created by anyone in public channels on the given team.
|
||||
func (s *SqlReactionStore) GetTopForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopReactionList, error) {
|
||||
var reactions []*model.TopReaction
|
||||
reactions := make([]*model.TopReaction, 0)
|
||||
|
||||
query := `
|
||||
SELECT
|
||||
@@ -241,34 +241,41 @@ func (s *SqlReactionStore) GetTopForTeamSince(teamID string, userID string, sinc
|
||||
FROM ((
|
||||
SELECT
|
||||
EmojiName,
|
||||
count(EmojiName) AS EmojiCount
|
||||
count(EmojiName) AS EmojiCount,
|
||||
Reactions.DeleteAt AS DeleteAt,
|
||||
Reactions.CreateAt AS CreateAt
|
||||
FROM
|
||||
ChannelMembers
|
||||
INNER JOIN Channels ON ChannelMembers.ChannelId = Channels.Id
|
||||
INNER JOIN Posts ON Channels.Id = Posts.ChannelId
|
||||
INNER JOIN Reactions ON Posts.Id = Reactions.PostId
|
||||
WHERE
|
||||
Reactions.DeleteAt = 0
|
||||
AND ChannelMembers.UserId = ?
|
||||
ChannelMembers.UserId = ?
|
||||
AND Channels.Type = 'P'
|
||||
AND Channels.TeamId = ?
|
||||
AND Reactions.CreateAt > ?
|
||||
GROUP BY
|
||||
Reactions.EmojiName)
|
||||
Reactions.EmojiName,
|
||||
Reactions.DeleteAt,
|
||||
Reactions.CreateAt)
|
||||
UNION ALL (
|
||||
SELECT
|
||||
EmojiName,
|
||||
count(EmojiName) AS EmojiCount
|
||||
count(EmojiName) AS EmojiCount,
|
||||
Reactions.DeleteAt AS DeleteAt,
|
||||
Reactions.CreateAt AS CreateAt
|
||||
FROM
|
||||
Reactions
|
||||
INNER JOIN Posts ON Reactions.PostId = Posts.Id
|
||||
INNER JOIN PublicChannels ON Posts.ChannelId = PublicChannels.Id
|
||||
WHERE
|
||||
Reactions.DeleteAt = 0
|
||||
AND PublicChannels.TeamId = ?
|
||||
AND Reactions.CreateAt > ?
|
||||
PublicChannels.TeamId = ?
|
||||
GROUP BY
|
||||
Reactions.EmojiName)) AS A
|
||||
Reactions.EmojiName,
|
||||
Reactions.DeleteAt,
|
||||
Reactions.CreateAt)) AS A
|
||||
WHERE
|
||||
DeleteAt = 0
|
||||
AND CreateAt > ?
|
||||
GROUP BY
|
||||
EmojiName
|
||||
ORDER BY
|
||||
@@ -277,18 +284,18 @@ func (s *SqlReactionStore) GetTopForTeamSince(teamID string, userID string, sinc
|
||||
LIMIT ?
|
||||
OFFSET ?`
|
||||
|
||||
if err := s.GetReplicaX().Select(&reactions, query, userID, teamID, since, teamID, since, limit+1, offset); err != nil {
|
||||
if err := s.GetReplicaX().Select(&reactions, query, userID, teamID, teamID, since, limit+1, offset); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get top Reactions")
|
||||
}
|
||||
|
||||
return model.GetTopReactionListWithRankAndPagination(reactions, limit, offset), nil
|
||||
return model.GetTopReactionListWithPagination(reactions, limit), nil
|
||||
}
|
||||
|
||||
// GetTopForUserSince returns the instance counts of the following Reactions sets:
|
||||
// a) those created by the given user in any channel type on the given team (across the workspace if no team is given), and
|
||||
// b) those created by the given user in DM or group channels.
|
||||
func (s *SqlReactionStore) GetTopForUserSince(userID string, teamID string, since int64, offset int, limit int) (*model.TopReactionList, error) {
|
||||
var reactions []*model.TopReaction
|
||||
reactions := make([]*model.TopReaction, 0)
|
||||
var args []interface{}
|
||||
var query string
|
||||
|
||||
@@ -339,7 +346,7 @@ func (s *SqlReactionStore) GetTopForUserSince(userID string, teamID string, sinc
|
||||
return nil, errors.Wrap(err, "failed to get top Reactions")
|
||||
}
|
||||
|
||||
return model.GetTopReactionListWithRankAndPagination(reactions, limit, offset), nil
|
||||
return model.GetTopReactionListWithPagination(reactions, limit), nil
|
||||
}
|
||||
|
||||
func (s *SqlReactionStore) saveReactionAndUpdatePost(transaction *sqlxTxWrapper, reaction *model.Reaction) error {
|
||||
|
||||
@@ -281,6 +281,10 @@ type ChannelStore interface {
|
||||
SetShared(channelId string, shared bool) error
|
||||
// GetTeamForChannel returns the team for a given channelID.
|
||||
GetTeamForChannel(channelID string) (*model.Team, error)
|
||||
|
||||
// Insights
|
||||
GetTopChannelsForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopChannelList, error)
|
||||
GetTopChannelsForUserSince(userID string, teamID string, since int64, offset int, limit int) (*model.TopChannelList, error)
|
||||
}
|
||||
|
||||
type ChannelMemberHistoryStore interface {
|
||||
|
||||
@@ -1508,6 +1508,52 @@ func (_m *ChannelStore) GetTeamMembersForChannel(channelID string) ([]string, er
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetTopChannelsForTeamSince provides a mock function with given fields: teamID, userID, since, offset, limit
|
||||
func (_m *ChannelStore) GetTopChannelsForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopChannelList, error) {
|
||||
ret := _m.Called(teamID, userID, since, offset, limit)
|
||||
|
||||
var r0 *model.TopChannelList
|
||||
if rf, ok := ret.Get(0).(func(string, string, int64, int, int) *model.TopChannelList); ok {
|
||||
r0 = rf(teamID, userID, since, offset, limit)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.TopChannelList)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, string, int64, int, int) error); ok {
|
||||
r1 = rf(teamID, userID, since, offset, limit)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetTopChannelsForUserSince provides a mock function with given fields: userID, teamID, since, offset, limit
|
||||
func (_m *ChannelStore) GetTopChannelsForUserSince(userID string, teamID string, since int64, offset int, limit int) (*model.TopChannelList, error) {
|
||||
ret := _m.Called(userID, teamID, since, offset, limit)
|
||||
|
||||
var r0 *model.TopChannelList
|
||||
if rf, ok := ret.Get(0).(func(string, string, int64, int, int) *model.TopChannelList); ok {
|
||||
r0 = rf(userID, teamID, since, offset, limit)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.TopChannelList)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, string, int64, int, int) error); ok {
|
||||
r1 = rf(userID, teamID, since, offset, limit)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GroupSyncedChannelCount provides a mock function with given fields:
|
||||
func (_m *ChannelStore) GroupSyncedChannelCount() (int64, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
@@ -1605,6 +1605,38 @@ func (s *TimerLayerChannelStore) GetTeamMembersForChannel(channelID string) ([]s
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerChannelStore) GetTopChannelsForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopChannelList, error) {
|
||||
start := timemodule.Now()
|
||||
|
||||
result, err := s.ChannelStore.GetTopChannelsForTeamSince(teamID, userID, since, offset, limit)
|
||||
|
||||
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.GetTopChannelsForTeamSince", success, elapsed)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerChannelStore) GetTopChannelsForUserSince(userID string, teamID string, since int64, offset int, limit int) (*model.TopChannelList, error) {
|
||||
start := timemodule.Now()
|
||||
|
||||
result, err := s.ChannelStore.GetTopChannelsForUserSince(userID, teamID, since, offset, limit)
|
||||
|
||||
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.GetTopChannelsForUserSince", success, elapsed)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerChannelStore) GroupSyncedChannelCount() (int64, error) {
|
||||
start := timemodule.Now()
|
||||
|
||||
|
||||
@@ -374,17 +374,6 @@ func (c *Context) RequireTimestamp() *Context {
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Context) RequireTimeRange() *Context {
|
||||
if c.Err != nil {
|
||||
return c
|
||||
}
|
||||
|
||||
if c.Params.TimeRange == 0 {
|
||||
c.SetInvalidURLParam("time_range")
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Context) RequireChannelId() *Context {
|
||||
if c.Err != nil {
|
||||
return c
|
||||
|
||||
+2
-6
@@ -31,7 +31,7 @@ type Params struct {
|
||||
TokenId string
|
||||
ThreadId string
|
||||
Timestamp int64
|
||||
TimeRange int64
|
||||
TimeRange string
|
||||
ChannelId string
|
||||
PostId string
|
||||
PolicyId string
|
||||
@@ -255,11 +255,7 @@ func ParamsFromRequest(r *http.Request) *Params {
|
||||
params.Timestamp = val
|
||||
}
|
||||
|
||||
if val, err := model.GetStartUnixMilliForTimeRange(query.Get("time_range")); err != nil {
|
||||
params.TimeRange = 0
|
||||
} else {
|
||||
params.TimeRange = val
|
||||
}
|
||||
params.TimeRange = query.Get("time_range")
|
||||
|
||||
if val, err := strconv.ParseBool(query.Get("permanent")); err == nil {
|
||||
params.Permanent = val
|
||||
|
||||
Reference in New Issue
Block a user