MM-45899: Insights: least active channels (#20796)
* Add api endpoints, app layers for top inactive channels with dummy store calls * Add store functions for top inactive channels * Add model, store, app tests. * Add client function and api tests * Add participants information to TopInactiveChannel * Translation fix * Style fix while writing response * Return channelmember IDs instead of profiles, query in batch avoiding inside the loop * Make the following changes - move DeleteAt to subqueries, to avoid select, group by - Remove TeamId from response - Count bots and webhook posts * SQL query lint fix, store test fix to include bot messages * make app-layers * Fix empty participant lists being sent as [""] * Track channel joins, to distinguish 0 activity channels vs new channels Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
8fd1762c3b
Коммит
6adbcc5d05
@@ -3642,6 +3642,41 @@ func (c *Client4) GetTopChannelsForUserSince(teamId string, timeRange string, pa
|
||||
return topChannels, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
// GetTopInactiveChannelsForTeamSince will return an ordered list of the top channels in a given team.
|
||||
func (c *Client4) GetTopInactiveChannelsForTeamSince(teamId string, timeRange string, page int, perPage int) (*TopInactiveChannelList, *Response, error) {
|
||||
query := fmt.Sprintf("?time_range=%v&page=%v&per_page=%v", timeRange, page, perPage)
|
||||
r, err := c.DoAPIGet(c.teamRoute(teamId)+"/top/inactive_channels"+query, "")
|
||||
if err != nil {
|
||||
return nil, BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
var topInactiveChannels *TopInactiveChannelList
|
||||
if jsonErr := json.NewDecoder(r.Body).Decode(&topInactiveChannels); jsonErr != nil {
|
||||
return nil, nil, NewAppError("GetTopInactiveChannelsForTeamSince", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return topInactiveChannels, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
// GetTopInactiveChannelsForUserSince will return an ordered list of your top channels in a given team.
|
||||
func (c *Client4) GetTopInactiveChannelsForUserSince(teamId string, timeRange string, page int, perPage int) (*TopInactiveChannelList, *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/inactive_channels"+query, "")
|
||||
if err != nil {
|
||||
return nil, BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
var topInactiveChannels *TopInactiveChannelList
|
||||
if jsonErr := json.NewDecoder(r.Body).Decode(&topInactiveChannels); jsonErr != nil {
|
||||
return nil, nil, NewAppError("GetTopInactiveChannelsForUserSince", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return topInactiveChannels, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
// Post Section
|
||||
|
||||
// CreatePost creates a post based on the provided post struct.
|
||||
|
||||
@@ -63,6 +63,22 @@ type TopChannel struct {
|
||||
MessageCount int64 `json:"message_count"`
|
||||
}
|
||||
|
||||
// Top Channels
|
||||
type TopInactiveChannelList struct {
|
||||
InsightsListData
|
||||
Items []*TopInactiveChannel `json:"items"`
|
||||
}
|
||||
|
||||
type TopInactiveChannel struct {
|
||||
ID string `json:"id"`
|
||||
Type ChannelType `json:"type"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Name string `json:"name"`
|
||||
LastActivityAt int64 `json:"last_activity_at"`
|
||||
Participants StringArray `json:"participants"`
|
||||
MessageCount int64 `json:"-"`
|
||||
}
|
||||
|
||||
// Top Threads
|
||||
type TopThreadList struct {
|
||||
InsightsListData
|
||||
@@ -286,6 +302,20 @@ func GetTopThreadListWithPagination(threads []*TopThread, limit int) *TopThreadL
|
||||
return &TopThreadList{InsightsListData: InsightsListData{HasNext: hasNext}, Items: threads}
|
||||
}
|
||||
|
||||
// GetTopInactiveChannelListWithPagination adds a rank to each item in the given list of TopInactiveChannel and checks if there is
|
||||
// another page that can be fetched based on the given limit and offset. The given list of TopInactiveChannel is assumed to be
|
||||
// sorted by Score. Returns a TopInactiveChannelList.
|
||||
func GetTopInactiveChannelListWithPagination(channels []*TopInactiveChannel, limit int) *TopInactiveChannelList {
|
||||
// Add pagination support
|
||||
var hasNext bool
|
||||
if (limit != 0) && (len(channels) == limit+1) {
|
||||
hasNext = true
|
||||
channels = channels[:len(channels)-1]
|
||||
}
|
||||
|
||||
return &TopInactiveChannelList{InsightsListData: InsightsListData{HasNext: hasNext}, Items: channels}
|
||||
}
|
||||
|
||||
// GetTopDMListWithPagination adds a rank to each item in the given list of TopDM and checks if there is
|
||||
// another page that can be fetched based on the given limit and offset. The given list of TopDM is assumed to be
|
||||
// sorted by MessageCount(score). Returns a TopDMList.
|
||||
|
||||
@@ -121,6 +121,43 @@ func TestGetTopThreadListWithPagination(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTopInactiveChannelListWithPagination(t *testing.T) {
|
||||
channels := []*TopInactiveChannel{
|
||||
{ID: NewId(), MessageCount: 2},
|
||||
{ID: NewId(), MessageCount: 5},
|
||||
{ID: NewId(), MessageCount: 7},
|
||||
{ID: NewId(), MessageCount: 80},
|
||||
{ID: NewId(), MessageCount: 85},
|
||||
{ID: NewId(), MessageCount: 92}}
|
||||
|
||||
hasNextTC := []struct {
|
||||
Description string
|
||||
Limit int
|
||||
Offset int
|
||||
Expected *TopInactiveChannelList
|
||||
}{
|
||||
{
|
||||
Description: "has one page",
|
||||
Limit: len(channels),
|
||||
Offset: 0,
|
||||
Expected: &TopInactiveChannelList{InsightsListData: InsightsListData{HasNext: false}, Items: channels},
|
||||
},
|
||||
{
|
||||
Description: "has more than one page",
|
||||
Limit: len(channels) - 1,
|
||||
Offset: 0,
|
||||
Expected: &TopInactiveChannelList{InsightsListData: InsightsListData{HasNext: true}, Items: channels},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range hasNextTC {
|
||||
t.Run(test.Description, func(t *testing.T) {
|
||||
actual := GetTopInactiveChannelListWithPagination(channels, test.Limit)
|
||||
assert.Equal(t, test.Expected.HasNext, actual.HasNext)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTopDMsListWithPagination(t *testing.T) {
|
||||
dms := []*TopDM{
|
||||
{SecondParticipant: &TopDMInsightUserInformation{InsightUserInformation: InsightUserInformation{Id: NewId()}}, MessageCount: 100},
|
||||
|
||||
Ссылка в новой задаче
Block a user