[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>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
7afc14de36
Коммит
109f4643c6
@@ -828,6 +828,7 @@ type AppIface interface {
|
||||
GetUsersByIds(userIDs []string, options *store.UserGetByIdsOpts) ([]*model.User, *model.AppError)
|
||||
GetUsersByUsernames(usernames []string, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError)
|
||||
GetUsersEtag(restrictionsHash string) string
|
||||
GetUsersForReporting(filter *model.UserReportOptions) ([]*model.UserReport, *model.AppError)
|
||||
GetUsersFromProfiles(options *model.UserGetOptions) ([]*model.User, *model.AppError)
|
||||
GetUsersInChannel(options *model.UserGetOptions) ([]*model.User, *model.AppError)
|
||||
GetUsersInChannelByAdmin(options *model.UserGetOptions) ([]*model.User, *model.AppError)
|
||||
|
||||
@@ -10704,6 +10704,28 @@ func (a *OpenTracingAppLayer) GetUsersEtag(restrictionsHash string) string {
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetUsersForReporting(filter *model.UserReportOptions) ([]*model.UserReport, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetUsersForReporting")
|
||||
|
||||
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.GetUsersForReporting(filter)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetUsersFromProfiles(options *model.UserGetOptions) ([]*model.User, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetUsersFromProfiles")
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/mattermost/mattermost/server/public/shared/i18n"
|
||||
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
||||
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||
pUtils "github.com/mattermost/mattermost/server/public/utils"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/app/email"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/app/imaging"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/app/users"
|
||||
@@ -2817,3 +2818,36 @@ func (a *App) UserIsFirstAdmin(user *model.User) bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *App) GetUsersForReporting(filter *model.UserReportOptions) ([]*model.UserReport, *model.AppError) {
|
||||
// Don't allow fetching more than 100 users at a time from the normal query endpoint
|
||||
if filter.PageSize <= 0 || filter.PageSize > 100 {
|
||||
return nil, model.NewAppError("GetUsersForReporting", "app.user.get_users_for_reporting.invalid_page_size", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// Validate date range
|
||||
if filter.EndAt > 0 && filter.StartAt > filter.EndAt {
|
||||
return nil, model.NewAppError("GetUsersForReporting", "app.user.get_users_for_reporting.bad_date_range", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
return a.getUserReport(filter)
|
||||
}
|
||||
|
||||
func (a *App) getUserReport(filter *model.UserReportOptions) ([]*model.UserReport, *model.AppError) {
|
||||
// Validate against the columns we allow sorting for
|
||||
if !pUtils.Contains(model.UserReportSortColumns, filter.SortColumn) {
|
||||
return nil, model.NewAppError("GetUsersForReporting", "app.user.get_user_report.invalid_sort_column", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
userReportQuery, err := a.Srv().Store().User().GetUserReport(filter)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetUsersForReporting", "app.user.get_user_report.store_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
userReports := make([]*model.UserReport, len(userReportQuery))
|
||||
for i, user := range userReportQuery {
|
||||
userReports[i] = user.ToReport()
|
||||
}
|
||||
|
||||
return userReports, nil
|
||||
}
|
||||
|
||||
@@ -1932,3 +1932,96 @@ func TestSendSubscriptionHistoryEvent(t *testing.T) {
|
||||
require.Equal(t, 10, subscriptionHistoryEvent.Seats, "Number of seats doesn't match")
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetUsersForReporting(t *testing.T) {
|
||||
t.Run("should throw error on invalid page size", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
userReports, err := th.App.GetUsersForReporting(&model.UserReportOptions{
|
||||
UserReportOptionsWithoutDateRange: model.UserReportOptionsWithoutDateRange{
|
||||
SortColumn: "Username",
|
||||
PageSize: 999,
|
||||
},
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Nil(t, userReports)
|
||||
})
|
||||
|
||||
t.Run("should throw error on invalid date range", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
userReports, err := th.App.GetUsersForReporting(&model.UserReportOptions{
|
||||
UserReportOptionsWithoutDateRange: model.UserReportOptionsWithoutDateRange{
|
||||
SortColumn: "Username",
|
||||
PageSize: 50,
|
||||
},
|
||||
StartAt: 1000,
|
||||
EndAt: 500,
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Nil(t, userReports)
|
||||
})
|
||||
|
||||
t.Run("should throw error on bad sort column", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
userReports, err := th.App.GetUsersForReporting(&model.UserReportOptions{
|
||||
UserReportOptionsWithoutDateRange: model.UserReportOptionsWithoutDateRange{
|
||||
SortColumn: "FakeColumn",
|
||||
PageSize: 50,
|
||||
},
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Nil(t, userReports)
|
||||
})
|
||||
|
||||
t.Run("should return some formatted reporting data", func(t *testing.T) {
|
||||
th := SetupWithStoreMock(t)
|
||||
defer th.TearDown()
|
||||
|
||||
// Mock to get the user count
|
||||
mockStore := th.App.Srv().Store().(*storemocks.Store)
|
||||
mockUserStore := storemocks.UserStore{}
|
||||
mockUserStore.On("GetUserReport",
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
).Return([]*model.UserReportQuery{
|
||||
{
|
||||
User: model.User{
|
||||
Id: "some-id",
|
||||
CreateAt: 1000,
|
||||
FirstName: "Bob",
|
||||
LastName: "Bobson",
|
||||
},
|
||||
UserPostStats: model.UserPostStats{
|
||||
LastLogin: 1500,
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
mockStore.On("User").Return(&mockUserStore)
|
||||
|
||||
userReports, err := th.App.GetUsersForReporting(&model.UserReportOptions{
|
||||
UserReportOptionsWithoutDateRange: model.UserReportOptionsWithoutDateRange{
|
||||
SortColumn: "Username",
|
||||
PageSize: 50,
|
||||
},
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, userReports)
|
||||
require.Equal(t, "Bob Bobson", userReports[0].DisplayName)
|
||||
})
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user