diff --git a/api4/team.go b/api4/team.go index 0fef9f0978..4625197a7d 100644 --- a/api4/team.go +++ b/api4/team.go @@ -374,6 +374,10 @@ func getTeamMembers(c *Context, w http.ResponseWriter, r *http.Request) { return } + sort := r.URL.Query().Get("sort") + excludeDeletedUsers := r.URL.Query().Get("exclude_deleted_users") + excludeDeletedUsersBool, _ := strconv.ParseBool(excludeDeletedUsers) + if !c.App.SessionHasPermissionToTeam(*c.App.Session(), c.Params.TeamId, model.PERMISSION_VIEW_TEAM) { c.SetPermissionError(model.PERMISSION_VIEW_TEAM) return @@ -385,7 +389,13 @@ func getTeamMembers(c *Context, w http.ResponseWriter, r *http.Request) { return } - members, err := c.App.GetTeamMembers(c.Params.TeamId, c.Params.Page*c.Params.PerPage, c.Params.PerPage, restrictions) + teamMembersGetOptions := &model.TeamMembersGetOptions{ + Sort: sort, + ExcludeDeletedUsers: excludeDeletedUsersBool, + ViewRestrictions: restrictions, + } + + members, err := c.App.GetTeamMembers(c.Params.TeamId, c.Params.Page*c.Params.PerPage, c.Params.PerPage, teamMembersGetOptions) if err != nil { c.Err = err return diff --git a/api4/team_test.go b/api4/team_test.go index cb83bc41ac..ae18c00eda 100644 --- a/api4/team_test.go +++ b/api4/team_test.go @@ -1295,7 +1295,19 @@ func TestGetTeamMembers(t *testing.T) { _, resp = Client.GetTeamMembers(team.Id, 0, 1, "") CheckUnauthorizedStatus(t, resp) - _, resp = th.SystemAdminClient.GetTeamMembers(team.Id, 0, 100, "") + _, resp = th.SystemAdminClient.GetTeamMembersSortAndWithoutDeletedUsers(team.Id, 0, 100, "", false, "") + CheckNoError(t, resp) + + _, resp = th.SystemAdminClient.GetTeamMembersSortAndWithoutDeletedUsers(team.Id, 0, 100, model.USERNAME, false, "") + CheckNoError(t, resp) + + _, resp = th.SystemAdminClient.GetTeamMembersSortAndWithoutDeletedUsers(team.Id, 0, 100, model.USERNAME, true, "") + CheckNoError(t, resp) + + _, resp = th.SystemAdminClient.GetTeamMembersSortAndWithoutDeletedUsers(team.Id, 0, 100, "", true, "") + CheckNoError(t, resp) + + _, resp = th.SystemAdminClient.GetTeamMembersSortAndWithoutDeletedUsers(team.Id, 0, 100, model.USERNAME, false, "") CheckNoError(t, resp) } diff --git a/app/app_iface.go b/app/app_iface.go index d7418e746c..7d1d0ab443 100644 --- a/app/app_iface.go +++ b/app/app_iface.go @@ -594,7 +594,7 @@ type AppIface interface { GetTeamIcon(team *model.Team) ([]byte, *model.AppError) GetTeamIdFromQuery(query url.Values) (string, *model.AppError) GetTeamMember(teamId, userId string) (*model.TeamMember, *model.AppError) - GetTeamMembers(teamId string, offset int, limit int, restrictions *model.ViewUsersRestrictions) ([]*model.TeamMember, *model.AppError) + GetTeamMembers(teamId string, offset int, limit int, teamMembersGetOptions *model.TeamMembersGetOptions) ([]*model.TeamMember, *model.AppError) GetTeamMembersByIds(teamId string, userIds []string, restrictions *model.ViewUsersRestrictions) ([]*model.TeamMember, *model.AppError) GetTeamMembersForUser(userId string) ([]*model.TeamMember, *model.AppError) GetTeamMembersForUserWithPagination(userId string, page, perPage int) ([]*model.TeamMember, *model.AppError) diff --git a/app/opentracing_layer.go b/app/opentracing_layer.go index d324ab2de8..d27c3edba3 100644 --- a/app/opentracing_layer.go +++ b/app/opentracing_layer.go @@ -7572,7 +7572,7 @@ func (a *OpenTracingAppLayer) GetTeamMember(teamId string, userId string) (*mode return resultVar0, resultVar1 } -func (a *OpenTracingAppLayer) GetTeamMembers(teamId string, offset int, limit int, restrictions *model.ViewUsersRestrictions) ([]*model.TeamMember, *model.AppError) { +func (a *OpenTracingAppLayer) GetTeamMembers(teamId string, offset int, limit int, teamMembersGetOptions *model.TeamMembersGetOptions) ([]*model.TeamMember, *model.AppError) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetTeamMembers") @@ -7584,7 +7584,7 @@ func (a *OpenTracingAppLayer) GetTeamMembers(teamId string, offset int, limit in }() defer span.Finish() - resultVar0, resultVar1 := a.app.GetTeamMembers(teamId, offset, limit, restrictions) + resultVar0, resultVar1 := a.app.GetTeamMembers(teamId, offset, limit, teamMembersGetOptions) if resultVar1 != nil { span.LogFields(spanlog.Error(resultVar1)) diff --git a/app/team.go b/app/team.go index b490f07ac3..ece759672d 100644 --- a/app/team.go +++ b/app/team.go @@ -763,8 +763,8 @@ func (a *App) GetTeamMembersForUserWithPagination(userId string, page, perPage i return a.Srv().Store.Team().GetTeamsForUserWithPagination(userId, page, perPage) } -func (a *App) GetTeamMembers(teamId string, offset int, limit int, restrictions *model.ViewUsersRestrictions) ([]*model.TeamMember, *model.AppError) { - return a.Srv().Store.Team().GetMembers(teamId, offset, limit, restrictions) +func (a *App) GetTeamMembers(teamId string, offset int, limit int, teamMembersGetOptions *model.TeamMembersGetOptions) ([]*model.TeamMember, *model.AppError) { + return a.Srv().Store.Team().GetMembers(teamId, offset, limit, teamMembersGetOptions) } func (a *App) GetTeamMembersByIds(teamId string, userIds []string, restrictions *model.ViewUsersRestrictions) ([]*model.TeamMember, *model.AppError) { @@ -1591,7 +1591,7 @@ func (a *App) ClearTeamMembersCache(teamID string) { page := 0 for { - teamMembers, err := a.Srv().Store.Team().GetMembers(teamID, page, perPage, &model.ViewUsersRestrictions{}) + teamMembers, err := a.Srv().Store.Team().GetMembers(teamID, page, perPage, nil) if err != nil { a.Log().Warn("error clearing cache for team members", mlog.String("team_id", teamID), mlog.String("err", err.Error())) break diff --git a/app/team_test.go b/app/team_test.go index 07d29638fe..7640911022 100644 --- a/app/team_test.go +++ b/app/team_test.go @@ -5,6 +5,7 @@ package app import ( "fmt" + "math/rand" "sort" "strings" "testing" @@ -825,15 +826,16 @@ func TestGetTeamMembers(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() - var userIDs sort.StringSlice - userIDs = append(userIDs, th.BasicUser.Id) - userIDs = append(userIDs, th.BasicUser2.Id) + var users []model.User + users = append(users, *th.BasicUser) + users = append(users, *th.BasicUser2) for i := 0; i < 8; i++ { user := model.User{ Email: strings.ToLower(model.NewId()) + "success+test@example.com", Username: fmt.Sprintf("user%v", i), Password: "passwd1", + DeleteAt: int64(rand.Intn(2)), } ruser, err := th.App.CreateUser(&user) require.Nil(t, err) @@ -843,25 +845,90 @@ func TestGetTeamMembers(t *testing.T) { _, err = th.App.AddUserToTeam(th.BasicTeam.Id, ruser.Id, "") require.Nil(t, err) - // Store the user ids for comparison later - userIDs = append(userIDs, ruser.Id) + // Store the users for comparison later + users = append(users, *ruser) } - // Sort them because the result of GetTeamMembers() is also sorted - sort.Sort(userIDs) - // Fetch team members multipile times - members, err := th.App.GetTeamMembers(th.BasicTeam.Id, 0, 5, nil) - require.Nil(t, err) + t.Run("Ensure Sorted By Username when TeamMemberGet options is passed", func(t *testing.T) { + members, err := th.App.GetTeamMembers(th.BasicTeam.Id, 0, 100, &model.TeamMembersGetOptions{Sort: model.USERNAME}) + require.Nil(t, err) - // This should return 5 members - members2, err := th.App.GetTeamMembers(th.BasicTeam.Id, 5, 6, nil) - require.Nil(t, err) - members = append(members, members2...) + // Sort the users array by username + sort.Slice(users, func(i, j int) bool { + return users[i].Username < users[j].Username + }) - require.Equal(t, len(userIDs), len(members)) - for i, member := range members { - assert.Equal(t, userIDs[i], member.UserId) - } + // We should have the same number of users in both users and members array as we have not excluded any deleted members + require.Equal(t, len(users), len(members)) + for i, member := range members { + assert.Equal(t, users[i].Id, member.UserId) + } + }) + + t.Run("Ensure ExcludedDeletedUsers when TeamMemberGetOptions is passed", func(t *testing.T) { + members, err := th.App.GetTeamMembers(th.BasicTeam.Id, 0, 100, &model.TeamMembersGetOptions{ExcludeDeletedUsers: true}) + require.Nil(t, err) + + // Choose all users who aren't deleted from our users array + var usersNotDeletedIDs []string + var membersIDs []string + for _, u := range users { + if u.DeleteAt == 0 { + usersNotDeletedIDs = append(usersNotDeletedIDs, u.Id) + } + } + + for _, m := range members { + membersIDs = append(membersIDs, m.UserId) + } + + require.Equal(t, len(usersNotDeletedIDs), len(membersIDs)) + require.ElementsMatch(t, usersNotDeletedIDs, membersIDs) + }) + + t.Run("Ensure Sorted By Username and ExcludedDeletedUsers when TeamMemberGetOptions is passed", func(t *testing.T) { + members, err := th.App.GetTeamMembers(th.BasicTeam.Id, 0, 100, &model.TeamMembersGetOptions{Sort: model.USERNAME, ExcludeDeletedUsers: true}) + require.Nil(t, err) + + var usersNotDeleted []model.User + for _, u := range users { + if u.DeleteAt == 0 { + usersNotDeleted = append(usersNotDeleted, u) + } + } + + // Sort our non deleted members by username + sort.Slice(usersNotDeleted, func(i, j int) bool { + return usersNotDeleted[i].Username < usersNotDeleted[j].Username + }) + + require.Equal(t, len(usersNotDeleted), len(members)) + for i, member := range members { + assert.Equal(t, usersNotDeleted[i].Id, member.UserId) + } + }) + + t.Run("Ensure Sorted By User ID when no TeamMemberGetOptions is passed", func(t *testing.T) { + + // Sort them by UserID because the result of GetTeamMembers() is also sorted + sort.Slice(users, func(i, j int) bool { + return users[i].Id < users[j].Id + }) + + // Fetch team members multipile times + members, err := th.App.GetTeamMembers(th.BasicTeam.Id, 0, 5, nil) + require.Nil(t, err) + + // This should return 5 members + members2, err := th.App.GetTeamMembers(th.BasicTeam.Id, 5, 6, nil) + require.Nil(t, err) + members = append(members, members2...) + + require.Equal(t, len(users), len(members)) + for i, member := range members { + assert.Equal(t, users[i].Id, member.UserId) + } + }) } func TestGetTeamStats(t *testing.T) { diff --git a/app/user_viewmembers_test.go b/app/user_viewmembers_test.go index 2a55a506c7..74a55092d1 100644 --- a/app/user_viewmembers_test.go +++ b/app/user_viewmembers_test.go @@ -1007,7 +1007,10 @@ func TestResctrictedViewMembers(t *testing.T) { for _, tc := range testCases { t.Run(tc.Name, func(t *testing.T) { - results, err := th.App.GetTeamMembers(tc.TeamId, 0, 100, tc.Restrictions) + getTeamMemberOptions := &model.TeamMembersGetOptions{ + ViewRestrictions: tc.Restrictions, + } + results, err := th.App.GetTeamMembers(tc.TeamId, 0, 100, getTeamMemberOptions) require.Nil(t, err) ids := []string{} for _, result := range results { diff --git a/model/client4.go b/model/client4.go index 0bdbb9343f..6de1879bcc 100644 --- a/model/client4.go +++ b/model/client4.go @@ -1805,6 +1805,18 @@ func (c *Client4) GetTeamMembers(teamId string, page int, perPage int, etag stri return TeamMembersFromJson(r.Body), BuildResponse(r) } +// GetTeamMembersWithoutDeletedUsers returns team members based on the provided team id string. Additional parameters of sort and exclude_deleted_users accepted as well +// Could not add it to above function due to it be a breaking change. +func (c *Client4) GetTeamMembersSortAndWithoutDeletedUsers(teamId string, page int, perPage int, sort string, exclude_deleted_users bool, etag string) ([]*TeamMember, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v&sort=%v&exclude_deleted_users=%v", page, perPage, sort, exclude_deleted_users) + r, err := c.DoApiGet(c.GetTeamMembersRoute(teamId)+query, etag) + if err != nil { + return nil, BuildErrorResponse(r, err) + } + defer closeBody(r) + return TeamMembersFromJson(r.Body), BuildResponse(r) +} + // GetTeamMembersForUser returns the team members for a user. func (c *Client4) GetTeamMembersForUser(userId string, etag string) ([]*TeamMember, *Response) { r, err := c.DoApiGet(c.GetUserRoute(userId)+"/teams/members", etag) diff --git a/model/team_member.go b/model/team_member.go index c00b1a3b03..1eab55a231 100644 --- a/model/team_member.go +++ b/model/team_member.go @@ -11,6 +11,10 @@ import ( "strings" ) +const ( + USERNAME = "Username" +) + type TeamMember struct { TeamId string `json:"team_id"` UserId string `json:"user_id"` @@ -44,6 +48,17 @@ type EmailInviteWithError struct { Error *AppError `json:"error"` } +type TeamMembersGetOptions struct { + // Sort the team members. Accepts "Username", but defaults to "Id". + Sort string + + // If true, exclude team members whose corresponding user is deleted. + ExcludeDeletedUsers bool + + // Restrict to search in a list of teams and channels + ViewRestrictions *ViewUsersRestrictions +} + func (o *TeamMember) ToJson() string { b, _ := json.Marshal(o) return string(b) diff --git a/store/opentracing_layer.go b/store/opentracing_layer.go index 28ea32bae6..420fd73041 100644 --- a/store/opentracing_layer.go +++ b/store/opentracing_layer.go @@ -6375,7 +6375,7 @@ func (s *OpenTracingLayerTeamStore) GetMember(teamId string, userId string) (*mo return resultVar0, resultVar1 } -func (s *OpenTracingLayerTeamStore) GetMembers(teamId string, offset int, limit int, restrictions *model.ViewUsersRestrictions) ([]*model.TeamMember, *model.AppError) { +func (s *OpenTracingLayerTeamStore) GetMembers(teamId string, offset int, limit int, teamMembersGetOptions *model.TeamMembersGetOptions) ([]*model.TeamMember, *model.AppError) { origCtx := s.Root.Store.Context() span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "TeamStore.GetMembers") s.Root.Store.SetContext(newCtx) @@ -6384,7 +6384,7 @@ func (s *OpenTracingLayerTeamStore) GetMembers(teamId string, offset int, limit }() defer span.Finish() - resultVar0, resultVar1 := s.TeamStore.GetMembers(teamId, offset, limit, restrictions) + resultVar0, resultVar1 := s.TeamStore.GetMembers(teamId, offset, limit, teamMembersGetOptions) if resultVar1 != nil { span.LogFields(spanlog.Error(resultVar1)) ext.Error.Set(span, true) diff --git a/store/sqlstore/team_store.go b/store/sqlstore/team_store.go index 987a186ac1..fe467c1488 100644 --- a/store/sqlstore/team_store.go +++ b/store/sqlstore/team_store.go @@ -847,15 +847,32 @@ func (s SqlTeamStore) GetMember(teamId string, userId string) (*model.TeamMember return dbMember.ToModel(), nil } -func (s SqlTeamStore) GetMembers(teamId string, offset int, limit int, restrictions *model.ViewUsersRestrictions) ([]*model.TeamMember, *model.AppError) { +func (s SqlTeamStore) GetMembers(teamId string, offset int, limit int, teamMembersGetOptions *model.TeamMembersGetOptions) ([]*model.TeamMember, *model.AppError) { query := s.getTeamMembersWithSchemeSelectQuery(). Where(sq.Eq{"TeamMembers.TeamId": teamId}). Where(sq.Eq{"TeamMembers.DeleteAt": 0}). - OrderBy("UserId"). Limit(uint64(limit)). Offset(uint64(offset)) - query = applyTeamMemberViewRestrictionsFilter(query, teamId, restrictions) + if teamMembersGetOptions == nil || teamMembersGetOptions.Sort == "" { + query = query.OrderBy("UserId") + } + + if teamMembersGetOptions != nil { + if teamMembersGetOptions.Sort == model.USERNAME || teamMembersGetOptions.ExcludeDeletedUsers { + query = query.LeftJoin("Users ON TeamMembers.UserId = Users.Id") + } + + if teamMembersGetOptions.ExcludeDeletedUsers { + query = query.Where(sq.Eq{"Users.DeleteAt": 0}) + } + + if teamMembersGetOptions.Sort == model.USERNAME { + query = query.OrderBy(model.USERNAME) + } + + query = applyTeamMemberViewRestrictionsFilter(query, teamId, teamMembersGetOptions.ViewRestrictions) + } queryString, args, err := query.ToSql() if err != nil { diff --git a/store/store.go b/store/store.go index 1bd183167a..d4665925ed 100644 --- a/store/store.go +++ b/store/store.go @@ -90,7 +90,7 @@ type TeamStore interface { UpdateMember(member *model.TeamMember) (*model.TeamMember, *model.AppError) UpdateMultipleMembers(members []*model.TeamMember) ([]*model.TeamMember, *model.AppError) GetMember(teamId string, userId string) (*model.TeamMember, *model.AppError) - GetMembers(teamId string, offset int, limit int, restrictions *model.ViewUsersRestrictions) ([]*model.TeamMember, *model.AppError) + GetMembers(teamId string, offset int, limit int, teamMembersGetOptions *model.TeamMembersGetOptions) ([]*model.TeamMember, *model.AppError) GetMembersByIds(teamId string, userIds []string, restrictions *model.ViewUsersRestrictions) ([]*model.TeamMember, *model.AppError) GetTotalMemberCount(teamId string, restrictions *model.ViewUsersRestrictions) (int64, *model.AppError) GetActiveMemberCount(teamId string, restrictions *model.ViewUsersRestrictions) (int64, *model.AppError) diff --git a/store/storetest/mocks/TeamStore.go b/store/storetest/mocks/TeamStore.go index f9740366d8..dba9cbca74 100644 --- a/store/storetest/mocks/TeamStore.go +++ b/store/storetest/mocks/TeamStore.go @@ -525,13 +525,13 @@ func (_m *TeamStore) GetMember(teamId string, userId string) (*model.TeamMember, return r0, r1 } -// GetMembers provides a mock function with given fields: teamId, offset, limit, restrictions -func (_m *TeamStore) GetMembers(teamId string, offset int, limit int, restrictions *model.ViewUsersRestrictions) ([]*model.TeamMember, *model.AppError) { - ret := _m.Called(teamId, offset, limit, restrictions) +// GetMembers provides a mock function with given fields: teamId, offset, limit, teamMembersGetOptions +func (_m *TeamStore) GetMembers(teamId string, offset int, limit int, teamMembersGetOptions *model.TeamMembersGetOptions) ([]*model.TeamMember, *model.AppError) { + ret := _m.Called(teamId, offset, limit, teamMembersGetOptions) var r0 []*model.TeamMember - if rf, ok := ret.Get(0).(func(string, int, int, *model.ViewUsersRestrictions) []*model.TeamMember); ok { - r0 = rf(teamId, offset, limit, restrictions) + if rf, ok := ret.Get(0).(func(string, int, int, *model.TeamMembersGetOptions) []*model.TeamMember); ok { + r0 = rf(teamId, offset, limit, teamMembersGetOptions) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*model.TeamMember) @@ -539,8 +539,8 @@ func (_m *TeamStore) GetMembers(teamId string, offset int, limit int, restrictio } var r1 *model.AppError - if rf, ok := ret.Get(1).(func(string, int, int, *model.ViewUsersRestrictions) *model.AppError); ok { - r1 = rf(teamId, offset, limit, restrictions) + if rf, ok := ret.Get(1).(func(string, int, int, *model.TeamMembersGetOptions) *model.AppError); ok { + r1 = rf(teamId, offset, limit, teamMembersGetOptions) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*model.AppError) diff --git a/store/storetest/team_store.go b/store/storetest/team_store.go index 3f8b7661e7..c11d24308a 100644 --- a/store/storetest/team_store.go +++ b/store/storetest/team_store.go @@ -46,13 +46,13 @@ func TestTeamStore(t *testing.T, ss store.Store) { t.Run("TeamPublicCount", func(t *testing.T) { testPublicTeamCount(t, ss) }) t.Run("TeamPrivateCount", func(t *testing.T) { testPrivateTeamCount(t, ss) }) t.Run("TeamMembers", func(t *testing.T) { testTeamMembers(t, ss) }) + t.Run("TestGetMembers", func(t *testing.T) { testGetMembers(t, ss) }) t.Run("SaveMember", func(t *testing.T) { testTeamSaveMember(t, ss) }) t.Run("SaveMultipleMembers", func(t *testing.T) { testTeamSaveMultipleMembers(t, ss) }) t.Run("UpdateMember", func(t *testing.T) { testTeamUpdateMember(t, ss) }) t.Run("UpdateMultipleMembers", func(t *testing.T) { testTeamUpdateMultipleMembers(t, ss) }) t.Run("RemoveMember", func(t *testing.T) { testTeamRemoveMember(t, ss) }) t.Run("RemoveMembers", func(t *testing.T) { testTeamRemoveMembers(t, ss) }) - t.Run("GetMembersOrder", func(t *testing.T) { testGetMembersOrder(t, ss) }) t.Run("SaveTeamMemberMaxMembers", func(t *testing.T) { testSaveTeamMemberMaxMembers(t, ss) }) t.Run("GetTeamMember", func(t *testing.T) { testGetTeamMember(t, ss) }) t.Run("GetTeamMembersByIds", func(t *testing.T) { testGetTeamMembersByIds(t, ss) }) @@ -875,28 +875,135 @@ func testTeamCount(t *testing.T, ss store.Store) { require.Equal(t, countNotIncludingDeleted+1, countIncludingDeleted) } -func testGetMembersOrder(t *testing.T, ss store.Store) { - teamId1 := model.NewId() - teamId2 := model.NewId() +func testGetMembers(t *testing.T, ss store.Store) { + // Each user should have a mention count of exactly 1 in the DB at this point. + t.Run("Test GetMembers Order By UserID", func(t *testing.T) { + teamId1 := model.NewId() + teamId2 := model.NewId() - m1 := &model.TeamMember{TeamId: teamId1, UserId: "55555555555555555555555555"} - m2 := &model.TeamMember{TeamId: teamId1, UserId: "11111111111111111111111111"} - m3 := &model.TeamMember{TeamId: teamId1, UserId: "33333333333333333333333333"} - m4 := &model.TeamMember{TeamId: teamId1, UserId: "22222222222222222222222222"} - m5 := &model.TeamMember{TeamId: teamId1, UserId: "44444444444444444444444444"} - m6 := &model.TeamMember{TeamId: teamId2, UserId: "00000000000000000000000000"} + m1 := &model.TeamMember{TeamId: teamId1, UserId: "55555555555555555555555555"} + m2 := &model.TeamMember{TeamId: teamId1, UserId: "11111111111111111111111111"} + m3 := &model.TeamMember{TeamId: teamId1, UserId: "33333333333333333333333333"} + m4 := &model.TeamMember{TeamId: teamId1, UserId: "22222222222222222222222222"} + m5 := &model.TeamMember{TeamId: teamId1, UserId: "44444444444444444444444444"} + m6 := &model.TeamMember{TeamId: teamId2, UserId: "00000000000000000000000000"} - _, err := ss.Team().SaveMultipleMembers([]*model.TeamMember{m1, m2, m3, m4, m5, m6}, -1) - require.Nil(t, err) + _, err := ss.Team().SaveMultipleMembers([]*model.TeamMember{m1, m2, m3, m4, m5, m6}, -1) + require.Nil(t, err) - ms, err := ss.Team().GetMembers(teamId1, 0, 100, nil) - require.Nil(t, err) - assert.Len(t, ms, 5) - assert.Equal(t, "11111111111111111111111111", ms[0].UserId) - assert.Equal(t, "22222222222222222222222222", ms[1].UserId) - assert.Equal(t, "33333333333333333333333333", ms[2].UserId) - assert.Equal(t, "44444444444444444444444444", ms[3].UserId) - assert.Equal(t, "55555555555555555555555555", ms[4].UserId) + // Gets users ordered by UserId + ms, err := ss.Team().GetMembers(teamId1, 0, 100, nil) + require.Nil(t, err) + assert.Len(t, ms, 5) + assert.Equal(t, "11111111111111111111111111", ms[0].UserId) + assert.Equal(t, "22222222222222222222222222", ms[1].UserId) + assert.Equal(t, "33333333333333333333333333", ms[2].UserId) + assert.Equal(t, "44444444444444444444444444", ms[3].UserId) + assert.Equal(t, "55555555555555555555555555", ms[4].UserId) + }) + + t.Run("Test GetMembers Order By Username And Exclude Deleted Members", func(t *testing.T) { + teamId1 := model.NewId() + teamId2 := model.NewId() + + u1 := &model.User{Username: "a", Email: MakeEmail(), DeleteAt: int64(1)} + u2 := &model.User{Username: "c", Email: MakeEmail()} + u3 := &model.User{Username: "b", Email: MakeEmail(), DeleteAt: int64(1)} + u4 := &model.User{Username: "f", Email: MakeEmail()} + u5 := &model.User{Username: "e", Email: MakeEmail(), DeleteAt: int64(1)} + u6 := &model.User{Username: "d", Email: MakeEmail()} + + u1, err := ss.User().Save(u1) + require.Nil(t, err) + u2, err = ss.User().Save(u2) + require.Nil(t, err) + u3, err = ss.User().Save(u3) + require.Nil(t, err) + u4, err = ss.User().Save(u4) + require.Nil(t, err) + u5, err = ss.User().Save(u5) + require.Nil(t, err) + u6, err = ss.User().Save(u6) + require.Nil(t, err) + + m1 := &model.TeamMember{TeamId: teamId1, UserId: u1.Id} + m2 := &model.TeamMember{TeamId: teamId1, UserId: u2.Id} + m3 := &model.TeamMember{TeamId: teamId1, UserId: u3.Id} + m4 := &model.TeamMember{TeamId: teamId1, UserId: u4.Id} + m5 := &model.TeamMember{TeamId: teamId1, UserId: u5.Id} + m6 := &model.TeamMember{TeamId: teamId2, UserId: u6.Id} + + _, err = ss.Team().SaveMultipleMembers([]*model.TeamMember{m1, m2, m3, m4, m5, m6}, -1) + require.Nil(t, err) + + // Gets users ordered by UserName + ms, err := ss.Team().GetMembers(teamId1, 0, 100, &model.TeamMembersGetOptions{Sort: model.USERNAME}) + require.Nil(t, err) + assert.Len(t, ms, 5) + assert.Equal(t, u1.Id, ms[0].UserId) + assert.Equal(t, u3.Id, ms[1].UserId) + assert.Equal(t, u2.Id, ms[2].UserId) + assert.Equal(t, u5.Id, ms[3].UserId) + assert.Equal(t, u4.Id, ms[4].UserId) + + // Gets users ordered by UserName and excludes deleted members + ms, err = ss.Team().GetMembers(teamId1, 0, 100, &model.TeamMembersGetOptions{Sort: model.USERNAME, ExcludeDeletedUsers: true}) + require.Nil(t, err) + assert.Len(t, ms, 2) + assert.Equal(t, u2.Id, ms[0].UserId) + assert.Equal(t, u4.Id, ms[1].UserId) + }) + + t.Run("Test GetMembers Excluded Deleted Users", func(t *testing.T) { + teamId1 := model.NewId() + teamId2 := model.NewId() + + u1 := &model.User{Email: MakeEmail()} + u2 := &model.User{Email: MakeEmail(), DeleteAt: int64(1)} + u3 := &model.User{Email: MakeEmail()} + u4 := &model.User{Email: MakeEmail(), DeleteAt: int64(3)} + u5 := &model.User{Email: MakeEmail()} + u6 := &model.User{Email: MakeEmail(), DeleteAt: int64(5)} + + u1, err := ss.User().Save(u1) + require.Nil(t, err) + u2, err = ss.User().Save(u2) + require.Nil(t, err) + u3, err = ss.User().Save(u3) + require.Nil(t, err) + u4, err = ss.User().Save(u4) + require.Nil(t, err) + u5, err = ss.User().Save(u5) + require.Nil(t, err) + u6, err = ss.User().Save(u6) + require.Nil(t, err) + + m1 := &model.TeamMember{TeamId: teamId1, UserId: u1.Id} + m2 := &model.TeamMember{TeamId: teamId1, UserId: u2.Id} + m3 := &model.TeamMember{TeamId: teamId1, UserId: u3.Id} + m4 := &model.TeamMember{TeamId: teamId1, UserId: u4.Id} + m5 := &model.TeamMember{TeamId: teamId1, UserId: u5.Id} + m6 := &model.TeamMember{TeamId: teamId2, UserId: u6.Id} + + t1, err := ss.Team().SaveMember(m1, -1) + require.Nil(t, err) + _, err = ss.Team().SaveMember(m2, -1) + require.Nil(t, err) + t3, err := ss.Team().SaveMember(m3, -1) + require.Nil(t, err) + _, err = ss.Team().SaveMember(m4, -1) + require.Nil(t, err) + t5, err := ss.Team().SaveMember(m5, -1) + require.Nil(t, err) + _, err = ss.Team().SaveMember(m6, -1) + require.Nil(t, err) + + // Gets users ordered by UserName + ms, err := ss.Team().GetMembers(teamId1, 0, 100, &model.TeamMembersGetOptions{ExcludeDeletedUsers: true}) + require.Nil(t, err) + assert.Len(t, ms, 3) + require.ElementsMatch(t, ms, [3]*model.TeamMember{t1, t3, t5}) + }) } func testTeamMembers(t *testing.T, ss store.Store) { diff --git a/store/timer_layer.go b/store/timer_layer.go index e026b8920c..e55f1239c0 100644 --- a/store/timer_layer.go +++ b/store/timer_layer.go @@ -5770,10 +5770,10 @@ func (s *TimerLayerTeamStore) GetMember(teamId string, userId string) (*model.Te return resultVar0, resultVar1 } -func (s *TimerLayerTeamStore) GetMembers(teamId string, offset int, limit int, restrictions *model.ViewUsersRestrictions) ([]*model.TeamMember, *model.AppError) { +func (s *TimerLayerTeamStore) GetMembers(teamId string, offset int, limit int, teamMembersGetOptions *model.TeamMembersGetOptions) ([]*model.TeamMember, *model.AppError) { start := timemodule.Now() - resultVar0, resultVar1 := s.TeamStore.GetMembers(teamId, offset, limit, restrictions) + resultVar0, resultVar1 := s.TeamStore.GetMembers(teamId, offset, limit, teamMembersGetOptions) elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second) if s.Root.Metrics != nil {