diff --git a/api4/emoji.go b/api4/emoji.go index 23bc5a98a7..8dc51d18ed 100644 --- a/api4/emoji.go +++ b/api4/emoji.go @@ -53,7 +53,7 @@ func createEmoji(c *Context, w http.ResponseWriter, r *http.Request) { defer c.LogAuditRec(auditRec) // Allow any user with CREATE_EMOJIS permission at Team level to create emojis at system level - memberships, err := c.App.GetTeamMembersForUser(c.AppContext.Session().UserId) + memberships, err := c.App.GetTeamMembersForUser(c.AppContext.Session().UserId, true) if err != nil { c.Err = err @@ -143,7 +143,7 @@ func deleteEmoji(c *Context, w http.ResponseWriter, r *http.Request) { auditRec.AddMeta("emoji", emoji) // Allow any user with DELETE_EMOJIS permission at Team level to delete emojis at system level - memberships, err := c.App.GetTeamMembersForUser(c.AppContext.Session().UserId) + memberships, err := c.App.GetTeamMembersForUser(c.AppContext.Session().UserId, true) if err != nil { c.Err = err diff --git a/api4/resolver.go b/api4/resolver.go index d0c138eec3..1ba05fb59a 100644 --- a/api4/resolver.go +++ b/api4/resolver.go @@ -172,7 +172,8 @@ func (r *resolver) TeamMembers(ctx context.Context, args struct { return []*teamMember{{*tm}}, nil } - members, appErr := c.App.GetTeamMembersForUser(args.UserID) + // Do not return archived team members + members, appErr := c.App.GetTeamMembersForUser(args.UserID, false) if appErr != nil { return nil, appErr } diff --git a/api4/resolver_team_member_test.go b/api4/resolver_team_member_test.go index de2aab8c08..674e01b073 100644 --- a/api4/resolver_team_member_test.go +++ b/api4/resolver_team_member_test.go @@ -248,5 +248,13 @@ func TestGraphQLTeamMembers(t *testing.T) { expectedTeams[i].Id = tm.Team.ID expectedTeams[i].DisplayName = tm.Team.DisplayName } + + // Removing from a team and ensuring we get the right response. + th.UnlinkUserFromTeam(th.BasicUser, myTeam) + resp, err = th.MakeGraphQLRequest(&input) + require.NoError(t, err) + require.Len(t, resp.Errors, 0) + require.NoError(t, json.Unmarshal(resp.Data, &q)) + assert.Len(t, q.TeamMembers, 1) }) } diff --git a/api4/team.go b/api4/team.go index cf353f2b8d..1cd78acff7 100644 --- a/api4/team.go +++ b/api4/team.go @@ -554,7 +554,7 @@ func getTeamMembersForUser(c *Context, w http.ResponseWriter, r *http.Request) { return } - members, err := c.App.GetTeamMembersForUser(c.Params.UserId) + members, err := c.App.GetTeamMembersForUser(c.Params.UserId, true) if err != nil { c.Err = err return diff --git a/app/app_iface.go b/app/app_iface.go index d25eede7c7..046547e02f 100644 --- a/app/app_iface.go +++ b/app/app_iface.go @@ -737,7 +737,7 @@ type AppIface interface { GetTeamMember(teamID, userID string) (*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) + GetTeamMembersForUser(userID string, includeDeleted bool) ([]*model.TeamMember, *model.AppError) GetTeamMembersForUserWithPagination(userID string, page, perPage int) ([]*model.TeamMember, *model.AppError) GetTeamPoliciesForUser(userID string, offset, limit int) (*model.RetentionPolicyForTeamList, *model.AppError) GetTeamStats(teamID string, restrictions *model.ViewUsersRestrictions) (*model.TeamStats, *model.AppError) diff --git a/app/import_functions.go b/app/import_functions.go index 9c9239e7e4..26fcc92e2b 100644 --- a/app/import_functions.go +++ b/app/import_functions.go @@ -737,7 +737,7 @@ func (a *App) importUserTeams(user *model.User, data *[]UserTeamImportData) *mod isGuestByTeamId := map[string]bool{} isUserByTeamId := map[string]bool{} isAdminByTeamId := map[string]bool{} - existingMemberships, nErr := a.Srv().Store.Team().GetTeamsForUser(context.Background(), user.Id) + existingMemberships, nErr := a.Srv().Store.Team().GetTeamsForUser(context.Background(), user.Id, true) if nErr != nil { return model.NewAppError("importUserTeams", "app.team.get_members.app_error", nil, nErr.Error(), http.StatusInternalServerError) } diff --git a/app/import_functions_test.go b/app/import_functions_test.go index e71beab2cf..6284d14287 100644 --- a/app/import_functions_test.go +++ b/app/import_functions_test.go @@ -1717,7 +1717,7 @@ func TestImportUserTeams(t *testing.T) { } else { require.Nil(t, err) } - teamMembers, nErr := th.App.Srv().Store.Team().GetTeamsForUser(context.Background(), user.Id) + teamMembers, nErr := th.App.Srv().Store.Team().GetTeamsForUser(context.Background(), user.Id, true) require.NoError(t, nErr) require.Len(t, teamMembers, tc.expectedUserTeams) if tc.expectedUserTeams == 1 { diff --git a/app/opentracing/opentracing_layer.go b/app/opentracing/opentracing_layer.go index 60be666e46..a99a692cf4 100644 --- a/app/opentracing/opentracing_layer.go +++ b/app/opentracing/opentracing_layer.go @@ -9200,7 +9200,7 @@ func (a *OpenTracingAppLayer) GetTeamMembersByIds(teamID string, userIDs []strin return resultVar0, resultVar1 } -func (a *OpenTracingAppLayer) GetTeamMembersForUser(userID string) ([]*model.TeamMember, *model.AppError) { +func (a *OpenTracingAppLayer) GetTeamMembersForUser(userID string, includeDeleted bool) ([]*model.TeamMember, *model.AppError) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetTeamMembersForUser") @@ -9212,7 +9212,7 @@ func (a *OpenTracingAppLayer) GetTeamMembersForUser(userID string) ([]*model.Tea }() defer span.Finish() - resultVar0, resultVar1 := a.app.GetTeamMembersForUser(userID) + resultVar0, resultVar1 := a.app.GetTeamMembersForUser(userID, includeDeleted) if resultVar1 != nil { span.LogFields(spanlog.Error(resultVar1)) diff --git a/app/team.go b/app/team.go index b3b131ab0b..c4cf583b4b 100644 --- a/app/team.go +++ b/app/team.go @@ -893,8 +893,8 @@ func (a *App) GetTeamMember(teamID, userID string) (*model.TeamMember, *model.Ap return teamMember, nil } -func (a *App) GetTeamMembersForUser(userID string) ([]*model.TeamMember, *model.AppError) { - teamMembers, err := a.Srv().Store.Team().GetTeamsForUser(context.Background(), userID) +func (a *App) GetTeamMembersForUser(userID string, includeDeleted bool) ([]*model.TeamMember, *model.AppError) { + teamMembers, err := a.Srv().Store.Team().GetTeamsForUser(context.Background(), userID, includeDeleted) if err != nil { return nil, model.NewAppError("GetTeamMembersForUser", "app.team.get_members.app_error", nil, err.Error(), http.StatusInternalServerError) } diff --git a/app/user.go b/app/user.go index 13313469ba..999ee5c332 100644 --- a/app/user.go +++ b/app/user.go @@ -2136,7 +2136,7 @@ func (a *App) PromoteGuestToUser(c *request.Context, user *model.User, requestor } } - teamMembers, err := a.GetTeamMembersForUser(user.Id) + teamMembers, err := a.GetTeamMembersForUser(user.Id, true) if err != nil { mlog.Warn("Failed to get team members for user on promote guest to user", mlog.Err(err)) } @@ -2180,7 +2180,7 @@ func (a *App) DemoteUserToGuest(user *model.User) *model.AppError { mlog.Warn("Unable to update user sessions", mlog.String("user_id", demotedUser.Id), mlog.Err(uErr)) } - teamMembers, err := a.GetTeamMembersForUser(user.Id) + teamMembers, err := a.GetTeamMembersForUser(user.Id, true) if err != nil { mlog.Warn("Failed to get team members for users on demote user to guest", mlog.Err(err)) } diff --git a/store/opentracinglayer/opentracinglayer.go b/store/opentracinglayer/opentracinglayer.go index 5f4431939b..f5c9e0bb92 100644 --- a/store/opentracinglayer/opentracinglayer.go +++ b/store/opentracinglayer/opentracinglayer.go @@ -8822,7 +8822,7 @@ func (s *OpenTracingLayerTeamStore) GetTeamsByUserId(userID string) ([]*model.Te return result, err } -func (s *OpenTracingLayerTeamStore) GetTeamsForUser(ctx context.Context, userID string) ([]*model.TeamMember, error) { +func (s *OpenTracingLayerTeamStore) GetTeamsForUser(ctx context.Context, userID string, includeDeleted bool) ([]*model.TeamMember, error) { origCtx := s.Root.Store.Context() span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "TeamStore.GetTeamsForUser") s.Root.Store.SetContext(newCtx) @@ -8831,7 +8831,7 @@ func (s *OpenTracingLayerTeamStore) GetTeamsForUser(ctx context.Context, userID }() defer span.Finish() - result, err := s.TeamStore.GetTeamsForUser(ctx, userID) + result, err := s.TeamStore.GetTeamsForUser(ctx, userID, includeDeleted) if err != nil { span.LogFields(spanlog.Error(err)) ext.Error.Set(span, true) diff --git a/store/retrylayer/retrylayer.go b/store/retrylayer/retrylayer.go index 87a57f4cfd..c57f6de2be 100644 --- a/store/retrylayer/retrylayer.go +++ b/store/retrylayer/retrylayer.go @@ -10072,11 +10072,11 @@ func (s *RetryLayerTeamStore) GetTeamsByUserId(userID string) ([]*model.Team, er } -func (s *RetryLayerTeamStore) GetTeamsForUser(ctx context.Context, userID string) ([]*model.TeamMember, error) { +func (s *RetryLayerTeamStore) GetTeamsForUser(ctx context.Context, userID string, includeDeleted bool) ([]*model.TeamMember, error) { tries := 0 for { - result, err := s.TeamStore.GetTeamsForUser(ctx, userID) + result, err := s.TeamStore.GetTeamsForUser(ctx, userID, includeDeleted) if err == nil { return result, nil } diff --git a/store/sqlstore/session_store.go b/store/sqlstore/session_store.go index 86b8600e16..daaff5e577 100644 --- a/store/sqlstore/session_store.go +++ b/store/sqlstore/session_store.go @@ -59,7 +59,7 @@ func (me SqlSessionStore) Save(session *model.Session) (*model.Session, error) { return nil, errors.Wrapf(err, "failed to save Session with id=%s", session.Id) } - teamMembers, err := me.Team().GetTeamsForUser(context.Background(), session.UserId) + teamMembers, err := me.Team().GetTeamsForUser(context.Background(), session.UserId, true) if err != nil { return nil, errors.Wrapf(err, "failed to find TeamMembers for Session with userId=%s", session.UserId) } @@ -87,7 +87,7 @@ func (me SqlSessionStore) Get(ctx context.Context, sessionIdOrToken string) (*mo tempMembers, err := me.Team().GetTeamsForUser( WithMaster(context.Background()), - session.UserId) + session.UserId, true) if err != nil { return nil, errors.Wrapf(err, "failed to find TeamMembers for Session with userId=%s", session.UserId) } @@ -107,7 +107,7 @@ func (me SqlSessionStore) GetSessions(userId string) ([]*model.Session, error) { return nil, errors.Wrapf(err, "failed to find Sessions with userId=%s", userId) } - teamMembers, err := me.Team().GetTeamsForUser(context.Background(), userId) + teamMembers, err := me.Team().GetTeamsForUser(context.Background(), userId, true) if err != nil { return nil, errors.Wrapf(err, "failed to find TeamMembers for Session with userId=%s", userId) } diff --git a/store/sqlstore/team_store.go b/store/sqlstore/team_store.go index d576afeb1f..4ac0500b4f 100644 --- a/store/sqlstore/team_store.go +++ b/store/sqlstore/team_store.go @@ -1081,10 +1081,14 @@ func (s SqlTeamStore) GetMembersByIds(teamId string, userIds []string, restricti } // GetTeamsForUser returns a list of teams that the user is a member of. Expects userId to be passed as a parameter. -func (s SqlTeamStore) GetTeamsForUser(ctx context.Context, userId string) ([]*model.TeamMember, error) { +func (s SqlTeamStore) GetTeamsForUser(ctx context.Context, userId string, includeDeleted bool) ([]*model.TeamMember, error) { query := s.getTeamMembersWithSchemeSelectQuery(). Where(sq.Eq{"TeamMembers.UserId": userId}) + if !includeDeleted { + query = query.Where(sq.Eq{"TeamMembers.DeleteAt": 0}) + } + queryString, args, err := query.ToSql() if err != nil { return nil, errors.Wrap(err, "team_tosql") diff --git a/store/store.go b/store/store.go index 9cd0e0b99c..e0a82154cf 100644 --- a/store/store.go +++ b/store/store.go @@ -126,7 +126,7 @@ type TeamStore interface { GetMembersByIds(teamID string, userIds []string, restrictions *model.ViewUsersRestrictions) ([]*model.TeamMember, error) GetTotalMemberCount(teamID string, restrictions *model.ViewUsersRestrictions) (int64, error) GetActiveMemberCount(teamID string, restrictions *model.ViewUsersRestrictions) (int64, error) - GetTeamsForUser(ctx context.Context, userID string) ([]*model.TeamMember, error) + GetTeamsForUser(ctx context.Context, userID string, includeDeleted bool) ([]*model.TeamMember, error) GetTeamsForUserWithPagination(userID string, page, perPage int) ([]*model.TeamMember, error) GetChannelUnreadsForAllTeams(excludeTeamID, userID string) ([]*model.ChannelUnread, error) GetChannelUnreadsForTeam(teamID, userID string) ([]*model.ChannelUnread, error) diff --git a/store/storetest/mocks/TeamStore.go b/store/storetest/mocks/TeamStore.go index 388c579dde..e243eb251e 100644 --- a/store/storetest/mocks/TeamStore.go +++ b/store/storetest/mocks/TeamStore.go @@ -535,13 +535,13 @@ func (_m *TeamStore) GetTeamsByUserId(userID string) ([]*model.Team, error) { return r0, r1 } -// GetTeamsForUser provides a mock function with given fields: ctx, userID -func (_m *TeamStore) GetTeamsForUser(ctx context.Context, userID string) ([]*model.TeamMember, error) { - ret := _m.Called(ctx, userID) +// GetTeamsForUser provides a mock function with given fields: ctx, userID, includeDeleted +func (_m *TeamStore) GetTeamsForUser(ctx context.Context, userID string, includeDeleted bool) ([]*model.TeamMember, error) { + ret := _m.Called(ctx, userID, includeDeleted) var r0 []*model.TeamMember - if rf, ok := ret.Get(0).(func(context.Context, string) []*model.TeamMember); ok { - r0 = rf(ctx, userID) + if rf, ok := ret.Get(0).(func(context.Context, string, bool) []*model.TeamMember); ok { + r0 = rf(ctx, userID, includeDeleted) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*model.TeamMember) @@ -549,8 +549,8 @@ func (_m *TeamStore) GetTeamsForUser(ctx context.Context, userID string) ([]*mod } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, userID) + if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok { + r1 = rf(ctx, userID, includeDeleted) } else { r1 = ret.Error(1) } diff --git a/store/storetest/team_store.go b/store/storetest/team_store.go index 2104aa41c8..57d746bd84 100644 --- a/store/storetest/team_store.go +++ b/store/storetest/team_store.go @@ -1318,7 +1318,7 @@ func testTeamMembers(t *testing.T, ss store.Store) { require.Equal(t, m3.UserId, ms[0].UserId) ctx := context.Background() - ms, err = ss.Team().GetTeamsForUser(ctx, m1.UserId) + ms, err = ss.Team().GetTeamsForUser(ctx, m1.UserId, true) require.NoError(t, err) require.Len(t, ms, 1) require.Equal(t, m1.TeamId, ms[0].TeamId) @@ -1347,14 +1347,26 @@ func testTeamMembers(t *testing.T, ss store.Store) { _, nErr = ss.Team().SaveMultipleMembers([]*model.TeamMember{m4, m5}, -1) require.NoError(t, nErr) - ms, err = ss.Team().GetTeamsForUser(ctx, uid) + ms, err = ss.Team().GetTeamsForUser(ctx, uid, true) require.NoError(t, err) require.Len(t, ms, 2) + m4.DeleteAt = model.GetMillis() + _, err = ss.Team().UpdateMember(m4) + require.NoError(t, err) + + ms, err = ss.Team().GetTeamsForUser(ctx, uid, true) + require.NoError(t, err) + require.Len(t, ms, 2) + + ms, err = ss.Team().GetTeamsForUser(ctx, uid, false) + require.NoError(t, err) + require.Len(t, ms, 1) + nErr = ss.Team().RemoveAllMembersByUser(uid) require.NoError(t, nErr) - ms, err = ss.Team().GetTeamsForUser(ctx, m1.UserId) + ms, err = ss.Team().GetTeamsForUser(ctx, m1.UserId, true) require.NoError(t, err) require.Empty(t, ms) } diff --git a/store/timerlayer/timerlayer.go b/store/timerlayer/timerlayer.go index c058bb40b2..0e4879658d 100644 --- a/store/timerlayer/timerlayer.go +++ b/store/timerlayer/timerlayer.go @@ -7944,10 +7944,10 @@ func (s *TimerLayerTeamStore) GetTeamsByUserId(userID string) ([]*model.Team, er return result, err } -func (s *TimerLayerTeamStore) GetTeamsForUser(ctx context.Context, userID string) ([]*model.TeamMember, error) { +func (s *TimerLayerTeamStore) GetTeamsForUser(ctx context.Context, userID string, includeDeleted bool) ([]*model.TeamMember, error) { start := timemodule.Now() - result, err := s.TeamStore.GetTeamsForUser(ctx, userID) + result, err := s.TeamStore.GetTeamsForUser(ctx, userID, includeDeleted) elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second) if s.Root.Metrics != nil {