[MM-55017] Add API method to get users for Admin Reporting (#25499)

* Add store method to get reporting data

* Some store changes

* Added app layer

* Added API call, some miscellaneous fixes

* Fix lint

* Fix serialized check

* Add API docs

* Fix user store tests leaking users

* Fix test

* PR feedback

* Add filtering for role/team/activated user, filter out bot users

* Fix mock

* Fix test

* Oops

* Switch to using struct filter

* More PR feedback

* Fix gen

* Fix test

* Fix API docs

* Fix test

* Fix possible SQL injection, some query optimization

* Fix migrations

* Oops

* Add role to API

* Fix check

* Add Client4 API call for load testing

* Fix test

* Update server/channels/store/storetest/user_store.go

Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com>

* PR feedback

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com>
Этот коммит содержится в:
Devin Binnie
2023-12-08 10:30:08 -05:00
коммит произвёл GitHub
родитель 7afc14de36
Коммит 109f4643c6
26 изменённых файлов: 2399 добавлений и 1 удалений

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

@@ -10,6 +10,7 @@ import (
"fmt"
"sort"
"strings"
"time"
"unicode/utf8"
sq "github.com/mattermost/squirrel"
@@ -2266,3 +2267,102 @@ func (us SqlUserStore) RefreshPostStatsForUsers() error {
return nil
}
func (us SqlUserStore) GetUserReport(filter *model.UserReportOptions) ([]*model.UserReportQuery, error) {
isPostgres := us.DriverName() == model.DatabaseDriverPostgres
selectColumns := []string{"u.Id", "u.LastLogin", "MAX(s.LastActivityAt) AS LastStatusAt"}
for _, column := range model.UserReportSortColumns {
selectColumns = append(selectColumns, "u."+column)
}
if isPostgres {
selectColumns = append(selectColumns,
"MAX(ps.LastPostDate) AS LastPostDate",
"COUNT(ps.Day) AS DaysActive",
"SUM(ps.NumPosts) AS TotalPosts",
)
} else {
selectColumns = append(selectColumns,
"MAX(p.CreateAt) AS LastPostDate",
"COUNT(DATE(FROM_UNIXTIME(p.CreateAt / 1000))) AS DaysActive",
"COUNT(p.Id) AS TotalPosts",
)
}
sortColumnValue := filter.SortColumn
if filter.SortDesc {
sortColumnValue += " DESC"
}
query := us.getQueryBuilder().
Select(selectColumns...).
From("Users u").
LeftJoin("Status s ON s.UserId = u.Id").
Where(sq.Or{
sq.Gt{filter.SortColumn: filter.LastSortColumnValue},
sq.And{
sq.Eq{filter.SortColumn: filter.LastSortColumnValue},
sq.Gt{"u.Id": filter.LastUserId},
},
}).
Where(sq.Expr("u.Id NOT IN (SELECT UserId FROM Bots)")).
GroupBy("u.Id").
OrderBy(sortColumnValue, "u.Id")
if filter.PageSize > 0 {
query = query.Limit(uint64(filter.PageSize))
}
if isPostgres {
query = query.LeftJoin("PostStats ps ON ps.UserId = u.Id")
if filter.StartAt > 0 {
startDate := time.UnixMilli(filter.StartAt)
query = query.Where(sq.Or{
sq.Expr("ps.UserId IS NULL"),
sq.GtOrEq{"ps.Day": startDate.Format("2006-01-02")},
})
}
if filter.EndAt > 0 {
endDate := time.UnixMilli(filter.EndAt)
query = query.Where(sq.Or{
sq.Expr("ps.UserId IS NULL"),
sq.Lt{"ps.Day": endDate.Format("2006-01-02")},
})
}
} else {
query = query.LeftJoin("Posts p on p.UserId = u.Id")
if filter.StartAt > 0 {
query = query.Where(sq.Or{
sq.Expr("p.UserId IS NULL"),
sq.GtOrEq{"p.CreateAt": filter.StartAt},
})
}
if filter.EndAt > 0 {
query = query.Where(sq.Or{
sq.Expr("p.UserId IS NULL"),
sq.Lt{"p.CreateAt": filter.EndAt},
})
}
}
query = applyRoleFilter(query, filter.Role, isPostgres)
if filter.HasNoTeam {
query = query.Where(sq.Expr("u.Id NOT IN (SELECT UserId FROM TeamMembers WHERE DeleteAt = 0)"))
} else if filter.Team != "" {
query = query.Join("TeamMembers tm ON (tm.UserId = u.Id AND tm.DeleteAt = 0)").
Where(sq.Eq{"tm.TeamId": filter.Team})
}
if filter.HideActive {
query = query.Where(sq.Gt{"u.DeleteAt": 0})
}
if filter.HideInactive {
query = query.Where(sq.Eq{"u.DeleteAt": 0})
}
userResults := []*model.UserReportQuery{}
err := us.GetReplicaX().SelectBuilder(&userResults, query)
if err != nil {
return nil, errors.Wrap(err, "failed to get users for reporting")
}
return userResults, nil
}