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