diff --git a/api4/group.go b/api4/group.go index 1dd3eabaa7..738cb8ea40 100644 --- a/api4/group.go +++ b/api4/group.go @@ -192,6 +192,10 @@ func linkGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) { return } + // Not awaiting completion because the group sync job executes the same procedure—but for all syncables—and + // persists the execution status to the jobs table. + go c.App.SyncRolesAndMembership(syncableID, syncableType) + w.WriteHeader(http.StatusCreated) b, marshalErr := json.Marshal(groupSyncable) @@ -340,6 +344,10 @@ func patchGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) { return } + // Not awaiting completion because the group sync job executes the same procedure—but for all syncables—and + // persists the execution status to the jobs table. + go c.App.SyncRolesAndMembership(syncableID, syncableType) + b, marshalErr := json.Marshal(groupSyncable) if marshalErr != nil { c.Err = model.NewAppError("Api4.patchGroupSyncable", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError) @@ -384,6 +392,10 @@ func unlinkGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) { return } + // Not awaiting completion because the group sync job executes the same procedure—but for all syncables—and + // persists the execution status to the jobs table. + go c.App.SyncRolesAndMembership(syncableID, syncableType) + ReturnStatusOK(w) } diff --git a/app/channel.go b/app/channel.go index 79ba1a0347..243058bb7e 100644 --- a/app/channel.go +++ b/app/channel.go @@ -2261,3 +2261,31 @@ func (a *App) FillInChannelsProps(channelList *model.ChannelList) *model.AppErro return nil } + +func (a *App) ClearChannelMembersCache(channelID string) { + perPage := 100 + page := 0 + + for { + channelMembers, err := a.Srv.Store.Channel().GetMembers(channelID, page, perPage) + if err != nil { + a.Log.Warn("error clearing cache for channel members", mlog.String("channel_id", channelID)) + break + } + + for _, channelMember := range *channelMembers { + a.ClearSessionCacheForUser(channelMember.UserId) + + message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_MEMBER_UPDATED, "", "", channelMember.UserId, nil) + message.Add("channelMember", channelMember.ToJson()) + a.Publish(message) + } + + length := len(*(channelMembers)) + if length < perPage { + break + } + + page++ + } +} diff --git a/app/group.go b/app/group.go index d7366c5c57..475f464ece 100644 --- a/app/group.go +++ b/app/group.go @@ -152,20 +152,20 @@ func (a *App) DeleteGroupSyncable(groupID string, syncableID string, syncableTyp return gs, nil } -func (a *App) TeamMembersToAdd(since int64) ([]*model.UserTeamIDPair, *model.AppError) { - return a.Srv.Store.Group().TeamMembersToAdd(since) +func (a *App) TeamMembersToAdd(since int64, teamID *string) ([]*model.UserTeamIDPair, *model.AppError) { + return a.Srv.Store.Group().TeamMembersToAdd(since, teamID) } -func (a *App) ChannelMembersToAdd(since int64) ([]*model.UserChannelIDPair, *model.AppError) { - return a.Srv.Store.Group().ChannelMembersToAdd(since) +func (a *App) ChannelMembersToAdd(since int64, channelID *string) ([]*model.UserChannelIDPair, *model.AppError) { + return a.Srv.Store.Group().ChannelMembersToAdd(since, channelID) } -func (a *App) TeamMembersToRemove() ([]*model.TeamMember, *model.AppError) { - return a.Srv.Store.Group().TeamMembersToRemove() +func (a *App) TeamMembersToRemove(teamID *string) ([]*model.TeamMember, *model.AppError) { + return a.Srv.Store.Group().TeamMembersToRemove(teamID) } -func (a *App) ChannelMembersToRemove() ([]*model.ChannelMember, *model.AppError) { - return a.Srv.Store.Group().ChannelMembersToRemove() +func (a *App) ChannelMembersToRemove(teamID *string) ([]*model.ChannelMember, *model.AppError) { + return a.Srv.Store.Group().ChannelMembersToRemove(teamID) } func (a *App) GetGroupsByChannel(channelId string, opts model.GroupSearchOpts) ([]*model.GroupWithSchemeAdmin, int, *model.AppError) { diff --git a/app/syncables.go b/app/syncables.go index 79c0a99741..53a21e37f4 100644 --- a/app/syncables.go +++ b/app/syncables.go @@ -4,30 +4,19 @@ package app import ( + "fmt" + "net/http" + "strings" + "github.com/mattermost/mattermost-server/v5/mlog" + "github.com/mattermost/mattermost-server/v5/model" ) -// CreateDefaultMemberships adds users to teams and channels based on their group memberships and how those groups are -// configured to sync with teams and channels for group members on or after the given timestamp. -func (a *App) CreateDefaultMemberships(since int64) error { - teamMembers, appErr := a.TeamMembersToAdd(since) - if appErr != nil { - return appErr - } - - for _, userTeam := range teamMembers { - _, err := a.AddTeamMember(userTeam.TeamID, userTeam.UserID) - if err != nil { - return err - } - - a.Log.Info("added teammember", - mlog.String("user_id", userTeam.UserID), - mlog.String("team_id", userTeam.TeamID), - ) - } - - channelMembers, appErr := a.ChannelMembersToAdd(since) +// createDefaultChannelMemberships adds users to channels based on their group memberships and how those groups are +// configured to sync with channels for group members on or after the given timestamp. If a channelID is given +// only that channel's members are created. If channelID is nil all channel memberships are created. +func (a *App) createDefaultChannelMemberships(since int64, channelID *string) error { + channelMembers, appErr := a.ChannelMembersToAdd(since, channelID) if appErr != nil { return appErr } @@ -76,10 +65,91 @@ func (a *App) CreateDefaultMemberships(since int64) error { return nil } +// createDefaultTeamMemberships adds users to teams based on their group memberships and how those groups are +// configured to sync with teams for group members on or after the given timestamp. If a teamID is given +// only that team's members are created. If teamID is nil all team memberships are created. +func (a *App) createDefaultTeamMemberships(since int64, teamID *string) error { + teamMembers, appErr := a.TeamMembersToAdd(since, teamID) + if appErr != nil { + return appErr + } + + for _, userTeam := range teamMembers { + _, err := a.AddTeamMember(userTeam.TeamID, userTeam.UserID) + if err != nil { + return err + } + + a.Log.Info("added teammember", + mlog.String("user_id", userTeam.UserID), + mlog.String("team_id", userTeam.TeamID), + ) + } + + return nil +} + +// CreateDefaultMemberships adds users to teams and channels based on their group memberships and how those groups +// are configured to sync with teams and channels for group members on or after the given timestamp. +func (a *App) CreateDefaultMemberships(since int64) error { + err := a.createDefaultTeamMemberships(since, nil) + if err != nil { + return err + } + + err = a.createDefaultChannelMemberships(since, nil) + if err != nil { + return err + } + + return nil +} + // DeleteGroupConstrainedMemberships deletes team and channel memberships of users who aren't members of the allowed // groups of all group-constrained teams and channels. func (a *App) DeleteGroupConstrainedMemberships() error { - channelMembers, appErr := a.ChannelMembersToRemove() + err := a.deleteGroupConstrainedChannelMemberships(nil) + if err != nil { + return err + } + + err = a.deleteGroupConstrainedTeamMemberships(nil) + if err != nil { + return err + } + + return nil +} + +// deleteGroupConstrainedTeamMemberships deletes team memberships of users who aren't members of the allowed +// groups of the given group-constrained team. If a teamID is given then the procedure is scoped to the given team, +// if teamID is nil then the proceedure affects all teams. +func (a *App) deleteGroupConstrainedTeamMemberships(teamID *string) error { + teamMembers, appErr := a.TeamMembersToRemove(teamID) + if appErr != nil { + return appErr + } + + for _, userTeam := range teamMembers { + err := a.RemoveUserFromTeam(userTeam.TeamId, userTeam.UserId, "") + if err != nil { + return err + } + + a.Log.Info("removed teammember", + mlog.String("user_id", userTeam.UserId), + mlog.String("team_id", userTeam.TeamId), + ) + } + + return nil +} + +// deleteGroupConstrainedChannelMemberships deletes channel memberships of users who aren't members of the allowed +// groups of the given group-constrained channel. If a channelID is given then the procedure is scoped to the given team, +// if channelID is nil then the proceedure affects all teams. +func (a *App) deleteGroupConstrainedChannelMemberships(channelID *string) error { + channelMembers, appErr := a.ChannelMembersToRemove(channelID) if appErr != nil { return appErr } @@ -101,22 +171,62 @@ func (a *App) DeleteGroupConstrainedMemberships() error { ) } - teamMembers, appErr := a.TeamMembersToRemove() - if appErr != nil { - return appErr + return nil +} + +// SyncSyncableRoles updates the SchemeAdmin field value of the given syncable's members based on the configuration of +// the member's group memberships and the configuration of those groups to the syncable. This method should only +// be invoked on group-synced (aka group-constrained) syncables. +func (a *App) SyncSyncableRoles(syncableID string, syncableType model.GroupSyncableType) *model.AppError { + permittedAdmins, err := a.Srv.Store.Group().PermittedSyncableAdmins(syncableID, syncableType) + if err != nil { + return err } - for _, userTeam := range teamMembers { - err := a.RemoveUserFromTeam(userTeam.TeamId, userTeam.UserId, "") - if err != nil { - return err - } + a.Log.Info( + fmt.Sprintf("Permitted admins for %s", syncableType), + mlog.String(strings.ToLower(fmt.Sprintf("%s_id", syncableType)), syncableID), + mlog.Any("permitted_admins", permittedAdmins), + ) - a.Log.Info("removed teammember", - mlog.String("user_id", userTeam.UserId), - mlog.String("team_id", userTeam.TeamId), - ) + var updateFunc func(string, []string) *model.AppError + + switch syncableType { + case model.GroupSyncableTypeTeam: + updateFunc = a.Srv.Store.Team().UpdateMembersRole + case model.GroupSyncableTypeChannel: + updateFunc = a.Srv.Store.Channel().UpdateMembersRole + default: + return model.NewAppError("App.SyncSyncableRoles", "groups.unsupported_syncable_type", map[string]interface{}{"Value": syncableType}, "", http.StatusInternalServerError) + } + + err = updateFunc(syncableID, permittedAdmins) + if err != nil { + return err } return nil } + +// SyncRolesAndMembership updates the SchemeAdmin status and membership of all of the members of the given +// syncable. +func (a *App) SyncRolesAndMembership(syncableID string, syncableType model.GroupSyncableType) { + a.SyncSyncableRoles(syncableID, syncableType) + + lastJob, _ := a.Srv.Store.Job().GetNewestJobByStatusAndType(model.JOB_STATUS_SUCCESS, model.JOB_TYPE_LDAP_SYNC) + var since int64 + if lastJob != nil { + since = lastJob.StartAt + } + + switch syncableType { + case model.GroupSyncableTypeTeam: + a.createDefaultTeamMemberships(since, &syncableID) + a.deleteGroupConstrainedTeamMemberships(&syncableID) + a.ClearTeamMembersCache(syncableID) + case model.GroupSyncableTypeChannel: + a.createDefaultChannelMemberships(since, &syncableID) + a.deleteGroupConstrainedChannelMemberships(&syncableID) + a.ClearChannelMembersCache(syncableID) + } +} diff --git a/app/syncables_test.go b/app/syncables_test.go index e6e2272400..f3c4e146ac 100644 --- a/app/syncables_test.go +++ b/app/syncables_test.go @@ -396,3 +396,70 @@ func TestDeleteGroupMemberships(t *testing.T) { require.Len(t, (*cmembers), 1) require.Equal(t, th.SystemAdminUser.Id, (*cmembers)[0].UserId) } + +func TestSyncSyncableRoles(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + team := th.CreateTeam() + + channel := th.CreateChannel(team) + channel.GroupConstrained = model.NewBool(true) + channel, err := th.App.UpdateChannel(channel) + require.Nil(t, err) + + user1 := th.CreateUser() + user2 := th.CreateUser() + group := th.CreateGroup() + + teamSyncable, err := th.App.UpsertGroupSyncable(&model.GroupSyncable{ + SyncableId: team.Id, + Type: model.GroupSyncableTypeTeam, + GroupId: group.Id, + }) + require.Nil(t, err) + + channelSyncable, err := th.App.UpsertGroupSyncable(&model.GroupSyncable{ + SyncableId: channel.Id, + Type: model.GroupSyncableTypeChannel, + GroupId: group.Id, + }) + require.Nil(t, err) + + for _, user := range []*model.User{user1, user2} { + _, err = th.App.UpsertGroupMember(group.Id, user.Id) + require.Nil(t, err) + + var tm *model.TeamMember + tm, err = th.App.AddTeamMember(team.Id, user.Id) + require.Nil(t, err) + require.False(t, tm.SchemeAdmin) + + cm := th.AddUserToChannel(user, channel) + require.False(t, cm.SchemeAdmin) + } + + teamSyncable.SchemeAdmin = true + _, err = th.App.UpdateGroupSyncable(teamSyncable) + require.Nil(t, err) + + channelSyncable.SchemeAdmin = true + _, err = th.App.UpdateGroupSyncable(channelSyncable) + require.Nil(t, err) + + err = th.App.SyncSyncableRoles(channel.Id, model.GroupSyncableTypeChannel) + require.Nil(t, err) + + err = th.App.SyncSyncableRoles(team.Id, model.GroupSyncableTypeTeam) + require.Nil(t, err) + + for _, user := range []*model.User{user1, user2} { + tm, err := th.App.GetTeamMember(team.Id, user.Id) + require.Nil(t, err) + require.True(t, tm.SchemeAdmin) + + cm, err := th.App.GetChannelMember(channel.Id, user.Id) + require.Nil(t, err) + require.True(t, cm.SchemeAdmin) + } +} diff --git a/app/team.go b/app/team.go index e9ec165058..44adf4db5e 100644 --- a/app/team.go +++ b/app/team.go @@ -1494,3 +1494,31 @@ func (a *App) InvalidateAllEmailInvites() *model.AppError { } return nil } + +func (a *App) ClearTeamMembersCache(teamID string) { + perPage := 100 + page := 0 + + for { + teamMembers, err := a.Srv.Store.Team().GetMembers(teamID, page, perPage, &model.ViewUsersRestrictions{}) + if err != nil { + a.Log.Warn("error clearing cache for team members", mlog.String("team_id", teamID)) + break + } + + for _, teamMember := range teamMembers { + a.ClearSessionCacheForUser(teamMember.UserId) + + message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_MEMBERROLE_UPDATED, "", "", teamMember.UserId, nil) + message.Add("member", teamMember.ToJson()) + a.Publish(message) + } + + length := len(teamMembers) + if length < perPage { + break + } + + page++ + } +} diff --git a/i18n/en.json b/i18n/en.json index 2274b01fad..0c7cba4b1f 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -4274,6 +4274,10 @@ "id": "ent.saml.service_disable.app_error", "translation": "SAML 2.0 is not configured or supported on this server." }, + { + "id": "groups.unsupported_syncable_type", + "translation": "Unsupported syncable type '{{.Value}}'." + }, { "id": "interactive_message.decode_trigger_id.base64_decode_failed", "translation": "Failed to decode base64 for trigger ID for interactive dialog." diff --git a/store/sqlstore/group_store.go b/store/sqlstore/group_store.go index 37350a8d00..967afb358d 100644 --- a/store/sqlstore/group_store.go +++ b/store/sqlstore/group_store.go @@ -633,37 +633,35 @@ func (s *SqlGroupStore) DeleteGroupSyncable(groupID string, syncableID string, s return groupSyncable, nil } -// TeamMembersToAdd returns a slice of UserTeamIDPair that need newly created memberships -// based on the groups configurations. -// -// Typically since will be the last successful group sync time. -func (s *SqlGroupStore) TeamMembersToAdd(since int64) ([]*model.UserTeamIDPair, *model.AppError) { - sql := ` - SELECT - GroupMembers.UserId, GroupTeams.TeamId - FROM - GroupMembers - JOIN GroupTeams - ON GroupTeams.GroupId = GroupMembers.GroupId - JOIN UserGroups ON UserGroups.Id = GroupMembers.GroupId - JOIN Teams ON Teams.Id = GroupTeams.TeamId - LEFT OUTER JOIN TeamMembers - ON - TeamMembers.TeamId = GroupTeams.TeamId - AND TeamMembers.UserId = GroupMembers.UserId - WHERE - TeamMembers.UserId IS NULL - AND UserGroups.DeleteAt = 0 - AND GroupTeams.DeleteAt = 0 - AND GroupTeams.AutoAdd = true - AND GroupMembers.DeleteAt = 0 - AND Teams.DeleteAt = 0 - AND (GroupMembers.CreateAt >= :Since - OR GroupTeams.UpdateAt >= :Since)` +func (s *SqlGroupStore) TeamMembersToAdd(since int64, teamID *string) ([]*model.UserTeamIDPair, *model.AppError) { + query := s.getQueryBuilder().Select("GroupMembers.UserId", "GroupTeams.TeamId"). + From("GroupMembers"). + Join("GroupTeams ON GroupTeams.GroupId = GroupMembers.GroupId"). + Join("UserGroups ON UserGroups.Id = GroupMembers.GroupId"). + Join("Teams ON Teams.Id = GroupTeams.TeamId"). + JoinClause("LEFT OUTER JOIN TeamMembers ON TeamMembers.TeamId = GroupTeams.TeamId AND TeamMembers.UserId = GroupMembers.UserId"). + Where(sq.Eq{ + "TeamMembers.UserId": nil, + "UserGroups.DeleteAt": 0, + "GroupTeams.DeleteAt": 0, + "GroupTeams.AutoAdd": true, + "GroupMembers.DeleteAt": 0, + "Teams.DeleteAt": 0, + }). + Where("(GroupMembers.CreateAt >= ? OR GroupTeams.UpdateAt >= ?)", since, since) + + if teamID != nil { + query = query.Where(sq.Eq{"Teams.Id": *teamID}) + } + + sql, params, err := query.ToSql() + if err != nil { + return nil, model.NewAppError("SqlGroupStore.TeamMembersToAdd", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError) + } var teamMembers []*model.UserTeamIDPair - _, err := s.GetReplica().Select(&teamMembers, sql, map[string]interface{}{"Since": since}) + _, err = s.GetReplica().Select(&teamMembers, sql, params...) if err != nil { return nil, model.NewAppError("SqlGroupStore.TeamMembersToAdd", "store.select_error", nil, err.Error(), http.StatusInternalServerError) } @@ -671,37 +669,36 @@ func (s *SqlGroupStore) TeamMembersToAdd(since int64) ([]*model.UserTeamIDPair, return teamMembers, nil } -// ChannelMembersToAdd returns a slice of UserChannelIDPair that need newly created memberships -// based on the groups configurations. -// -// Typically since will be the last successful group sync time. -func (s *SqlGroupStore) ChannelMembersToAdd(since int64) ([]*model.UserChannelIDPair, *model.AppError) { - sql := ` - SELECT - GroupMembers.UserId, GroupChannels.ChannelId - FROM - GroupMembers - JOIN GroupChannels ON GroupChannels.GroupId = GroupMembers.GroupId - JOIN UserGroups ON UserGroups.Id = GroupMembers.GroupId - JOIN Channels ON Channels.Id = GroupChannels.ChannelId - LEFT OUTER JOIN ChannelMemberHistory - ON - ChannelMemberHistory.ChannelId = GroupChannels.ChannelId - AND ChannelMemberHistory.UserId = GroupMembers.UserId - WHERE - ChannelMemberHistory.UserId IS NULL - AND ChannelMemberHistory.LeaveTime IS NULL - AND UserGroups.DeleteAt = 0 - AND GroupChannels.DeleteAt = 0 - AND GroupChannels.AutoAdd = true - AND GroupMembers.DeleteAt = 0 - AND Channels.DeleteAt = 0 - AND (GroupMembers.CreateAt >= :Since - OR GroupChannels.UpdateAt >= :Since)` +func (s *SqlGroupStore) ChannelMembersToAdd(since int64, channelID *string) ([]*model.UserChannelIDPair, *model.AppError) { + query := s.getQueryBuilder().Select("GroupMembers.UserId", "GroupChannels.ChannelId"). + From("GroupMembers"). + Join("GroupChannels ON GroupChannels.GroupId = GroupMembers.GroupId"). + Join("UserGroups ON UserGroups.Id = GroupMembers.GroupId"). + Join("Channels ON Channels.Id = GroupChannels.ChannelId"). + JoinClause("LEFT OUTER JOIN ChannelMemberHistory ON ChannelMemberHistory.ChannelId = GroupChannels.ChannelId AND ChannelMemberHistory.UserId = GroupMembers.UserId"). + Where(sq.Eq{ + "ChannelMemberHistory.UserId": nil, + "ChannelMemberHistory.LeaveTime": nil, + "UserGroups.DeleteAt": 0, + "GroupChannels.DeleteAt": 0, + "GroupChannels.AutoAdd": true, + "GroupMembers.DeleteAt": 0, + "Channels.DeleteAt": 0, + }). + Where("(GroupMembers.CreateAt >= ? OR GroupChannels.UpdateAt >= ?)", since, since) + + if channelID != nil { + query = query.Where(sq.Eq{"Channels.Id": *channelID}) + } + + sql, params, err := query.ToSql() + if err != nil { + return nil, model.NewAppError("SqlGroupStore.ChannelMembersToAdd", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError) + } var channelMembers []*model.UserChannelIDPair - _, err := s.GetReplica().Select(&channelMembers, sql, map[string]interface{}{"Since": since}) + _, err = s.GetReplica().Select(&channelMembers, sql, params...) if err != nil { return nil, model.NewAppError("SqlGroupStore.ChannelMembersToAdd", "store.select_error", nil, "", http.StatusInternalServerError) } @@ -723,48 +720,56 @@ func groupSyncableToGroupChannel(groupSyncable *model.GroupSyncable) *groupChann } } -// TeamMembersToRemove returns all team members that should be removed based on group constraints. -func (s *SqlGroupStore) TeamMembersToRemove() ([]*model.TeamMember, *model.AppError) { - sql := ` - SELECT - TeamMembers.TeamId, - TeamMembers.UserId, - TeamMembers.Roles, - TeamMembers.DeleteAt, - TeamMembers.SchemeUser, - TeamMembers.SchemeAdmin, - (TeamMembers.SchemeGuest IS NOT NULL AND TeamMembers.SchemeGuest) as SchemeGuest - FROM - TeamMembers - JOIN Teams ON Teams.Id = TeamMembers.TeamId - LEFT JOIN Bots ON Bots.UserId = TeamMembers.UserId - WHERE - TeamMembers.DeleteAt = 0 - AND Teams.DeleteAt = 0 - AND Teams.GroupConstrained = TRUE - AND Bots.UserId IS NULL - AND (TeamMembers.TeamId, TeamMembers.UserId) - NOT IN ( - SELECT - Teams.Id AS TeamId, GroupMembers.UserId - FROM - Teams - JOIN GroupTeams ON GroupTeams.TeamId = Teams.Id - JOIN UserGroups ON UserGroups.Id = GroupTeams.GroupId - JOIN GroupMembers ON GroupMembers.GroupId = UserGroups.Id - WHERE - Teams.GroupConstrained = TRUE - AND GroupTeams.DeleteAt = 0 - AND UserGroups.DeleteAt = 0 - AND Teams.DeleteAt = 0 - AND GroupMembers.DeleteAt = 0 - GROUP BY - Teams.Id, - GroupMembers.UserId)` +func (s *SqlGroupStore) TeamMembersToRemove(teamID *string) ([]*model.TeamMember, *model.AppError) { + whereStmt := ` + (TeamMembers.TeamId, + TeamMembers.UserId) + NOT IN ( + SELECT + Teams.Id AS TeamId, + GroupMembers.UserId + FROM + Teams + JOIN GroupTeams ON GroupTeams.TeamId = Teams.Id + JOIN UserGroups ON UserGroups.Id = GroupTeams.GroupId + JOIN GroupMembers ON GroupMembers.GroupId = UserGroups.Id + WHERE + Teams.GroupConstrained = TRUE + AND GroupTeams.DeleteAt = 0 + AND UserGroups.DeleteAt = 0 + AND Teams.DeleteAt = 0 + AND GroupMembers.DeleteAt = 0 + GROUP BY + Teams.Id, + GroupMembers.UserId)` + + query := s.getQueryBuilder().Select( + "TeamMembers.TeamId", + "TeamMembers.UserId", + "TeamMembers.Roles", + "TeamMembers.DeleteAt", + "TeamMembers.SchemeUser", + "TeamMembers.SchemeAdmin", + "(TeamMembers.SchemeGuest IS NOT NULL AND TeamMembers.SchemeGuest) AS SchemeGuest", + ). + From("TeamMembers"). + Join("Teams ON Teams.Id = TeamMembers.TeamId"). + LeftJoin("Bots ON Bots.UserId = TeamMembers.UserId"). + Where(sq.Eq{"TeamMembers.DeleteAt": 0, "Teams.DeleteAt": 0, "Teams.GroupConstrained": true, "Bots.UserId": nil}). + Where(whereStmt) + + if teamID != nil { + query = query.Where(sq.Eq{"TeamMembers.TeamId": *teamID}) + } + + sql, params, err := query.ToSql() + if err != nil { + return nil, model.NewAppError("SqlGroupStore.TeamMembersToRemove", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError) + } var teamMembers []*model.TeamMember - _, err := s.GetReplica().Select(&teamMembers, sql) + _, err = s.GetReplica().Select(&teamMembers, sql, params...) if err != nil { return nil, model.NewAppError("SqlGroupStore.TeamMembersToRemove", "store.select_error", nil, "", http.StatusInternalServerError) } @@ -811,51 +816,60 @@ func (s *SqlGroupStore) GetGroupsByChannel(channelId string, opts model.GroupSea return groups, nil } -// ChannelMembersToRemove returns all channel members that should be removed based on group constraints. -func (s *SqlGroupStore) ChannelMembersToRemove() ([]*model.ChannelMember, *model.AppError) { - sql := ` - SELECT - ChannelMembers.ChannelId, - ChannelMembers.UserId, - ChannelMembers.LastViewedAt, - ChannelMembers.MsgCount, - ChannelMembers.MentionCount, - ChannelMembers.NotifyProps, - ChannelMembers.LastUpdateAt, - ChannelMembers.LastUpdateAt, - ChannelMembers.SchemeUser, - ChannelMembers.SchemeAdmin, - (ChannelMembers.SchemeGuest IS NOT NULL AND ChannelMembers.SchemeGuest) as SchemeGuest - FROM - ChannelMembers - JOIN Channels ON Channels.Id = ChannelMembers.ChannelId - LEFT JOIN Bots ON Bots.UserId = ChannelMembers.UserId - WHERE - Channels.DeleteAt = 0 - AND Channels.GroupConstrained = TRUE - AND Bots.UserId IS NULL - AND (ChannelMembers.ChannelId, ChannelMembers.UserId) - NOT IN ( - SELECT - Channels.Id AS ChannelId, GroupMembers.UserId - FROM - Channels - JOIN GroupChannels ON GroupChannels.ChannelId = Channels.Id - JOIN UserGroups ON UserGroups.Id = GroupChannels.GroupId - JOIN GroupMembers ON GroupMembers.GroupId = UserGroups.Id - WHERE - Channels.GroupConstrained = TRUE - AND GroupChannels.DeleteAt = 0 - AND UserGroups.DeleteAt = 0 - AND Channels.DeleteAt = 0 - AND GroupMembers.DeleteAt = 0 - GROUP BY - Channels.Id, - GroupMembers.UserId)` +func (s *SqlGroupStore) ChannelMembersToRemove(channelID *string) ([]*model.ChannelMember, *model.AppError) { + whereStmt := ` + (ChannelMembers.ChannelId, + ChannelMembers.UserId) + NOT IN ( + SELECT + Channels.Id AS ChannelId, + GroupMembers.UserId + FROM + Channels + JOIN GroupChannels ON GroupChannels.ChannelId = Channels.Id + JOIN UserGroups ON UserGroups.Id = GroupChannels.GroupId + JOIN GroupMembers ON GroupMembers.GroupId = UserGroups.Id + WHERE + Channels.GroupConstrained = TRUE + AND GroupChannels.DeleteAt = 0 + AND UserGroups.DeleteAt = 0 + AND Channels.DeleteAt = 0 + AND GroupMembers.DeleteAt = 0 + GROUP BY + Channels.Id, + GroupMembers.UserId)` + + query := s.getQueryBuilder().Select( + "ChannelMembers.ChannelId", + "ChannelMembers.UserId", + "ChannelMembers.LastViewedAt", + "ChannelMembers.MsgCount", + "ChannelMembers.MentionCount", + "ChannelMembers.NotifyProps", + "ChannelMembers.LastUpdateAt", + "ChannelMembers.LastUpdateAt", + "ChannelMembers.SchemeUser", + "ChannelMembers.SchemeAdmin", + "(ChannelMembers.SchemeGuest IS NOT NULL AND ChannelMembers.SchemeGuest) AS SchemeGuest", + ). + From("ChannelMembers"). + Join("Channels ON Channels.Id = ChannelMembers.ChannelId"). + LeftJoin("Bots ON Bots.UserId = ChannelMembers.UserId"). + Where(sq.Eq{"Channels.DeleteAt": 0, "Channels.GroupConstrained": true, "Bots.UserId": nil}). + Where(whereStmt) + + if channelID != nil { + query = query.Where(sq.Eq{"ChannelMembers.ChannelId": *channelID}) + } + + sql, params, err := query.ToSql() + if err != nil { + return nil, model.NewAppError("SqlGroupStore.ChannelMembersToRemove", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError) + } var channelMembers []*model.ChannelMember - _, err := s.GetReplica().Select(&channelMembers, sql) + _, err = s.GetReplica().Select(&channelMembers, sql, params...) if err != nil { return nil, model.NewAppError("SqlGroupStore.ChannelMembersToRemove", "store.select_error", nil, err.Error(), http.StatusInternalServerError) } @@ -973,12 +987,12 @@ func (s *SqlGroupStore) GetGroups(page, perPage int, opts model.GroupSearchOpts) if len(opts.NotAssociatedToTeam) == 26 { groupsQuery = groupsQuery.Where(` g.Id NOT IN ( - SELECT - Id - FROM + SELECT + Id + FROM UserGroups JOIN GroupTeams ON GroupTeams.GroupId = UserGroups.Id - WHERE + WHERE GroupTeams.DeleteAt = 0 AND UserGroups.DeleteAt = 0 AND GroupTeams.TeamId = ? @@ -989,12 +1003,12 @@ func (s *SqlGroupStore) GetGroups(page, perPage int, opts model.GroupSearchOpts) if len(opts.NotAssociatedToChannel) == 26 { groupsQuery = groupsQuery.Where(` g.Id NOT IN ( - SELECT - Id - FROM + SELECT + Id + FROM UserGroups JOIN GroupChannels ON GroupChannels.GroupId = UserGroups.Id - WHERE + WHERE GroupChannels.DeleteAt = 0 AND UserGroups.DeleteAt = 0 AND GroupChannels.ChannelId = ? @@ -1173,17 +1187,17 @@ func (s *SqlGroupStore) AdminRoleGroupsForSyncableMember(userID, syncableID stri var groupIds []string sql := fmt.Sprintf(` - SELECT + SELECT GroupMembers.GroupId - FROM - GroupMembers - INNER JOIN + FROM + GroupMembers + INNER JOIN Group%[1]ss ON Group%[1]ss.GroupId = GroupMembers.GroupId - WHERE - GroupMembers.UserId = :UserId - AND GroupMembers.DeleteAt = 0 - AND %[1]sId = :%[1]sId - AND Group%[1]ss.DeleteAt = 0 + WHERE + GroupMembers.UserId = :UserId + AND GroupMembers.DeleteAt = 0 + AND %[1]sId = :%[1]sId + AND Group%[1]ss.DeleteAt = 0 AND Group%[1]ss.SchemeAdmin = TRUE`, syncableType) _, err := s.GetReplica().Select(&groupIds, sql, map[string]interface{}{"UserId": userID, fmt.Sprintf("%sId", syncableType): syncableID}) diff --git a/store/store.go b/store/store.go index 169cb6eb68..4dbfce6ad6 100644 --- a/store/store.go +++ b/store/store.go @@ -601,11 +601,23 @@ type GroupStore interface { UpdateGroupSyncable(groupSyncable *model.GroupSyncable) (*model.GroupSyncable, *model.AppError) DeleteGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) (*model.GroupSyncable, *model.AppError) - TeamMembersToAdd(since int64) ([]*model.UserTeamIDPair, *model.AppError) - ChannelMembersToAdd(since int64) ([]*model.UserChannelIDPair, *model.AppError) + // TeamMembersToAdd returns a slice of UserTeamIDPair that need newly created memberships + // based on the groups configurations. The returned list can be optionally scoped to a single given team. + // + // Typically since will be the last successful group sync time. + TeamMembersToAdd(since int64, teamID *string) ([]*model.UserTeamIDPair, *model.AppError) - TeamMembersToRemove() ([]*model.TeamMember, *model.AppError) - ChannelMembersToRemove() ([]*model.ChannelMember, *model.AppError) + // ChannelMembersToAdd returns a slice of UserChannelIDPair that need newly created memberships + // based on the groups configurations. The returned list can be optionally scoped to a single given channel. + // + // Typically since will be the last successful group sync time. + ChannelMembersToAdd(since int64, channelID *string) ([]*model.UserChannelIDPair, *model.AppError) + + // TeamMembersToRemove returns all team members that should be removed based on group constraints. + TeamMembersToRemove(teamID *string) ([]*model.TeamMember, *model.AppError) + + // ChannelMembersToRemove returns all channel members that should be removed based on group constraints. + ChannelMembersToRemove(channelID *string) ([]*model.ChannelMember, *model.AppError) GetGroupsByChannel(channelId string, opts model.GroupSearchOpts) ([]*model.GroupWithSchemeAdmin, *model.AppError) CountGroupsByChannel(channelId string, opts model.GroupSearchOpts) (int64, *model.AppError) diff --git a/store/storetest/group_store.go b/store/storetest/group_store.go index df48ceb70c..cd8eb2713f 100644 --- a/store/storetest/group_store.go +++ b/store/storetest/group_store.go @@ -41,11 +41,17 @@ func TestGroupStore(t *testing.T, ss store.Store) { t.Run("UpdateGroupSyncable", func(t *testing.T) { testUpdateGroupSyncable(t, ss) }) t.Run("DeleteGroupSyncable", func(t *testing.T) { testDeleteGroupSyncable(t, ss) }) - t.Run("TeamMembersToAdd", func(t *testing.T) { testPendingAutoAddTeamMembers(t, ss) }) - t.Run("ChannelMembersToAdd", func(t *testing.T) { testPendingAutoAddChannelMembers(t, ss) }) + t.Run("TeamMembersToAdd", func(t *testing.T) { testTeamMembersToAdd(t, ss) }) + t.Run("TeamMembersToAdd_SingleTeam", func(t *testing.T) { testTeamMembersToAddSingleTeam(t, ss) }) - t.Run("TeamMembersToRemove", func(t *testing.T) { testTeamMemberRemovals(t, ss) }) - t.Run("ChannelMembersToRemove", func(t *testing.T) { testChannelMemberRemovals(t, ss) }) + t.Run("ChannelMembersToAdd", func(t *testing.T) { testChannelMembersToAdd(t, ss) }) + t.Run("ChannelMembersToAdd_SingleChannel", func(t *testing.T) { testChannelMembersToAddSingleChannel(t, ss) }) + + t.Run("TeamMembersToRemove", func(t *testing.T) { testTeamMembersToRemove(t, ss) }) + t.Run("TeamMembersToRemove_SingleTeam", func(t *testing.T) { testTeamMembersToRemoveSingleTeam(t, ss) }) + + t.Run("ChannelMembersToRemove", func(t *testing.T) { testChannelMembersToRemove(t, ss) }) + t.Run("ChannelMembersToRemove_SingleChannel", func(t *testing.T) { testChannelMembersToRemoveSingleChannel(t, ss) }) t.Run("GetGroupsByChannel", func(t *testing.T) { testGetGroupsByChannel(t, ss) }) t.Run("GetGroupsByTeam", func(t *testing.T) { testGetGroupsByTeam(t, ss) }) @@ -1067,7 +1073,7 @@ func testDeleteGroupSyncable(t *testing.T, ss store.Store) { require.Equal(t, err.Id, "store.sql_group.group_syncable_already_deleted") } -func testPendingAutoAddTeamMembers(t *testing.T, ss store.Store) { +func testTeamMembersToAdd(t *testing.T, ss store.Store) { // Create Group group, err := ss.Group().Create(&model.Group{ Name: model.NewId(), @@ -1108,14 +1114,14 @@ func testPendingAutoAddTeamMembers(t *testing.T, ss store.Store) { require.Nil(t, err) // Time before syncable was created - teamMembers, err := ss.Group().TeamMembersToAdd(syncable.CreateAt - 1) + teamMembers, err := ss.Group().TeamMembersToAdd(syncable.CreateAt-1, nil) require.Nil(t, err) require.Len(t, teamMembers, 1) require.Equal(t, user.Id, teamMembers[0].UserID) require.Equal(t, team.Id, teamMembers[0].TeamID) // Time after syncable was created - teamMembers, err = ss.Group().TeamMembersToAdd(syncable.CreateAt + 1) + teamMembers, err = ss.Group().TeamMembersToAdd(syncable.CreateAt+1, nil) require.Nil(t, err) require.Empty(t, teamMembers) @@ -1124,7 +1130,7 @@ func testPendingAutoAddTeamMembers(t *testing.T, ss store.Store) { require.Nil(t, err) _, err = ss.Group().UpsertMember(group.Id, user.Id) require.Nil(t, err) - teamMembers, err = ss.Group().TeamMembersToAdd(syncable.CreateAt + 1) + teamMembers, err = ss.Group().TeamMembersToAdd(syncable.CreateAt+1, nil) require.Nil(t, err) require.Len(t, teamMembers, 1) @@ -1134,14 +1140,14 @@ func testPendingAutoAddTeamMembers(t *testing.T, ss store.Store) { require.Nil(t, err) // Time before syncable was updated - teamMembers, err = ss.Group().TeamMembersToAdd(syncable.UpdateAt - 1) + teamMembers, err = ss.Group().TeamMembersToAdd(syncable.UpdateAt-1, nil) require.Nil(t, err) require.Len(t, teamMembers, 1) require.Equal(t, user.Id, teamMembers[0].UserID) require.Equal(t, team.Id, teamMembers[0].TeamID) // Time after syncable was updated - teamMembers, err = ss.Group().TeamMembersToAdd(syncable.UpdateAt + 1) + teamMembers, err = ss.Group().TeamMembersToAdd(syncable.UpdateAt+1, nil) require.Nil(t, err) require.Empty(t, teamMembers) @@ -1149,21 +1155,21 @@ func testPendingAutoAddTeamMembers(t *testing.T, ss store.Store) { syncable.AutoAdd = false _, err = ss.Group().UpdateGroupSyncable(syncable) require.Nil(t, err) - teamMembers, err = ss.Group().TeamMembersToAdd(0) + teamMembers, err = ss.Group().TeamMembersToAdd(0, nil) require.Nil(t, err) require.Empty(t, teamMembers) // reset state of syncable and verify _, err = ss.Group().UpdateGroupSyncable(&pristineSyncable) require.Nil(t, err) - teamMembers, err = ss.Group().TeamMembersToAdd(0) + teamMembers, err = ss.Group().TeamMembersToAdd(0, nil) require.Nil(t, err) require.Len(t, teamMembers, 1) // No result if Group deleted _, err = ss.Group().Delete(group.Id) require.Nil(t, err) - teamMembers, err = ss.Group().TeamMembersToAdd(0) + teamMembers, err = ss.Group().TeamMembersToAdd(0, nil) require.Nil(t, err) require.Empty(t, teamMembers) @@ -1171,7 +1177,7 @@ func testPendingAutoAddTeamMembers(t *testing.T, ss store.Store) { group.DeleteAt = 0 _, err = ss.Group().Update(group) require.Nil(t, err) - teamMembers, err = ss.Group().TeamMembersToAdd(0) + teamMembers, err = ss.Group().TeamMembersToAdd(0, nil) require.Nil(t, err) require.Len(t, teamMembers, 1) @@ -1179,7 +1185,7 @@ func testPendingAutoAddTeamMembers(t *testing.T, ss store.Store) { team.DeleteAt = model.GetMillis() team, err = ss.Team().Update(team) require.Nil(t, err) - teamMembers, err = ss.Group().TeamMembersToAdd(0) + teamMembers, err = ss.Group().TeamMembersToAdd(0, nil) require.Nil(t, err) require.Empty(t, teamMembers) @@ -1187,35 +1193,35 @@ func testPendingAutoAddTeamMembers(t *testing.T, ss store.Store) { team.DeleteAt = 0 team, err = ss.Team().Update(team) require.Nil(t, err) - teamMembers, err = ss.Group().TeamMembersToAdd(0) + teamMembers, err = ss.Group().TeamMembersToAdd(0, nil) require.Nil(t, err) require.Len(t, teamMembers, 1) // No result if GroupTeam deleted _, err = ss.Group().DeleteGroupSyncable(group.Id, team.Id, model.GroupSyncableTypeTeam) require.Nil(t, err) - teamMembers, err = ss.Group().TeamMembersToAdd(0) + teamMembers, err = ss.Group().TeamMembersToAdd(0, nil) require.Nil(t, err) require.Empty(t, teamMembers) // reset GroupTeam and verify _, err = ss.Group().UpdateGroupSyncable(&pristineSyncable) require.Nil(t, err) - teamMembers, err = ss.Group().TeamMembersToAdd(0) + teamMembers, err = ss.Group().TeamMembersToAdd(0, nil) require.Nil(t, err) require.Len(t, teamMembers, 1) // No result if GroupMember deleted _, err = ss.Group().DeleteMember(group.Id, user.Id) require.Nil(t, err) - teamMembers, err = ss.Group().TeamMembersToAdd(0) + teamMembers, err = ss.Group().TeamMembersToAdd(0, nil) require.Nil(t, err) require.Empty(t, teamMembers) // restore group member and verify _, err = ss.Group().UpsertMember(group.Id, user.Id) require.Nil(t, err) - teamMembers, err = ss.Group().TeamMembersToAdd(0) + teamMembers, err = ss.Group().TeamMembersToAdd(0, nil) require.Nil(t, err) require.Len(t, teamMembers, 1) @@ -1225,12 +1231,102 @@ func testPendingAutoAddTeamMembers(t *testing.T, ss store.Store) { UserId: user.Id, }, 999) require.Nil(t, err) - teamMembers, err = ss.Group().TeamMembersToAdd(0) + teamMembers, err = ss.Group().TeamMembersToAdd(0, nil) require.Nil(t, err) require.Empty(t, teamMembers) } -func testPendingAutoAddChannelMembers(t *testing.T, ss store.Store) { +func testTeamMembersToAddSingleTeam(t *testing.T, ss store.Store) { + group1, err := ss.Group().Create(&model.Group{ + Name: model.NewId(), + DisplayName: "TeamMembersToAdd Test Group", + RemoteId: model.NewId(), + Source: model.GroupSourceLdap, + }) + require.Nil(t, err) + + group2, err := ss.Group().Create(&model.Group{ + Name: model.NewId(), + DisplayName: "TeamMembersToAdd Test Group", + RemoteId: model.NewId(), + Source: model.GroupSourceLdap, + }) + require.Nil(t, err) + + user1 := &model.User{ + Email: MakeEmail(), + Username: model.NewId(), + } + user1, err = ss.User().Save(user1) + require.Nil(t, err) + + user2 := &model.User{ + Email: MakeEmail(), + Username: model.NewId(), + } + user2, err = ss.User().Save(user2) + require.Nil(t, err) + + user3 := &model.User{ + Email: MakeEmail(), + Username: model.NewId(), + } + user3, err = ss.User().Save(user3) + require.Nil(t, err) + + for _, user := range []*model.User{user1, user2} { + _, err = ss.Group().UpsertMember(group1.Id, user.Id) + require.Nil(t, err) + } + _, err = ss.Group().UpsertMember(group2.Id, user3.Id) + require.Nil(t, err) + + team1 := &model.Team{ + DisplayName: "Name", + Description: "Some description", + CompanyName: "Some company name", + AllowOpenInvite: false, + InviteId: "inviteid0", + Name: "z-z-" + model.NewId() + "a", + Email: "success+" + model.NewId() + "@simulator.amazonses.com", + Type: model.TEAM_OPEN, + } + team1, err = ss.Team().Save(team1) + require.Nil(t, err) + + team2 := &model.Team{ + DisplayName: "Name", + Description: "Some description", + CompanyName: "Some company name", + AllowOpenInvite: false, + InviteId: "inviteid0", + Name: "z-z-" + model.NewId() + "a", + Email: "success+" + model.NewId() + "@simulator.amazonses.com", + Type: model.TEAM_OPEN, + } + team2, err = ss.Team().Save(team2) + require.Nil(t, err) + + _, err = ss.Group().CreateGroupSyncable(model.NewGroupTeam(group1.Id, team1.Id, true)) + require.Nil(t, err) + + _, err = ss.Group().CreateGroupSyncable(model.NewGroupTeam(group2.Id, team2.Id, true)) + require.Nil(t, err) + + teamMembers, err := ss.Group().TeamMembersToAdd(0, nil) + require.Nil(t, err) + require.Len(t, teamMembers, 3) + + teamMembers, err = ss.Group().TeamMembersToAdd(0, &team1.Id) + require.Nil(t, err) + require.Len(t, teamMembers, 2) + + teamMembers, err = ss.Group().TeamMembersToAdd(0, &team2.Id) + require.Nil(t, err) + require.Len(t, teamMembers, 1) +} + +func testChannelMembersToAdd(t *testing.T, ss store.Store) { // Create Group group, err := ss.Group().Create(&model.Group{ Name: model.NewId(), @@ -1267,14 +1363,14 @@ func testPendingAutoAddChannelMembers(t *testing.T, ss store.Store) { require.Nil(t, err) // Time before syncable was created - channelMembers, err := ss.Group().ChannelMembersToAdd(syncable.CreateAt - 1) + channelMembers, err := ss.Group().ChannelMembersToAdd(syncable.CreateAt-1, nil) require.Nil(t, err) require.Len(t, channelMembers, 1) require.Equal(t, user.Id, channelMembers[0].UserID) require.Equal(t, channel.Id, channelMembers[0].ChannelID) // Time after syncable was created - channelMembers, err = ss.Group().ChannelMembersToAdd(syncable.CreateAt + 1) + channelMembers, err = ss.Group().ChannelMembersToAdd(syncable.CreateAt+1, nil) require.Nil(t, err) require.Empty(t, channelMembers) @@ -1283,7 +1379,7 @@ func testPendingAutoAddChannelMembers(t *testing.T, ss store.Store) { require.Nil(t, err) _, err = ss.Group().UpsertMember(group.Id, user.Id) require.Nil(t, err) - channelMembers, err = ss.Group().ChannelMembersToAdd(syncable.CreateAt + 1) + channelMembers, err = ss.Group().ChannelMembersToAdd(syncable.CreateAt+1, nil) require.Nil(t, err) require.Len(t, channelMembers, 1) @@ -1293,14 +1389,14 @@ func testPendingAutoAddChannelMembers(t *testing.T, ss store.Store) { require.Nil(t, err) // Time before syncable was updated - channelMembers, err = ss.Group().ChannelMembersToAdd(syncable.UpdateAt - 1) + channelMembers, err = ss.Group().ChannelMembersToAdd(syncable.UpdateAt-1, nil) require.Nil(t, err) require.Len(t, channelMembers, 1) require.Equal(t, user.Id, channelMembers[0].UserID) require.Equal(t, channel.Id, channelMembers[0].ChannelID) // Time after syncable was updated - channelMembers, err = ss.Group().ChannelMembersToAdd(syncable.UpdateAt + 1) + channelMembers, err = ss.Group().ChannelMembersToAdd(syncable.UpdateAt+1, nil) require.Nil(t, err) require.Empty(t, channelMembers) @@ -1308,21 +1404,21 @@ func testPendingAutoAddChannelMembers(t *testing.T, ss store.Store) { syncable.AutoAdd = false _, err = ss.Group().UpdateGroupSyncable(syncable) require.Nil(t, err) - channelMembers, err = ss.Group().ChannelMembersToAdd(0) + channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil) require.Nil(t, err) require.Empty(t, channelMembers) // reset state of syncable and verify _, err = ss.Group().UpdateGroupSyncable(&pristineSyncable) require.Nil(t, err) - channelMembers, err = ss.Group().ChannelMembersToAdd(0) + channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil) require.Nil(t, err) require.Len(t, channelMembers, 1) // No result if Group deleted _, err = ss.Group().Delete(group.Id) require.Nil(t, err) - channelMembers, err = ss.Group().ChannelMembersToAdd(0) + channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil) require.Nil(t, err) require.Empty(t, channelMembers) @@ -1330,14 +1426,14 @@ func testPendingAutoAddChannelMembers(t *testing.T, ss store.Store) { group.DeleteAt = 0 _, err = ss.Group().Update(group) require.Nil(t, err) - channelMembers, err = ss.Group().ChannelMembersToAdd(0) + channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil) require.Nil(t, err) require.Len(t, channelMembers, 1) // No result if Channel deleted err = ss.Channel().Delete(channel.Id, model.GetMillis()) require.Nil(t, err) - channelMembers, err = ss.Group().ChannelMembersToAdd(0) + channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil) require.Nil(t, err) require.Empty(t, channelMembers) @@ -1345,65 +1441,145 @@ func testPendingAutoAddChannelMembers(t *testing.T, ss store.Store) { channel.DeleteAt = 0 _, err = ss.Channel().Update(channel) require.Nil(t, err) - channelMembers, err = ss.Group().ChannelMembersToAdd(0) + channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil) require.Nil(t, err) require.Len(t, channelMembers, 1) // No result if GroupChannel deleted _, err = ss.Group().DeleteGroupSyncable(group.Id, channel.Id, model.GroupSyncableTypeChannel) require.Nil(t, err) - channelMembers, err = ss.Group().ChannelMembersToAdd(0) + channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil) require.Nil(t, err) require.Empty(t, channelMembers) // reset GroupChannel and verify _, err = ss.Group().UpdateGroupSyncable(&pristineSyncable) require.Nil(t, err) - channelMembers, err = ss.Group().ChannelMembersToAdd(0) + channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil) require.Nil(t, err) require.Len(t, channelMembers, 1) // No result if GroupMember deleted _, err = ss.Group().DeleteMember(group.Id, user.Id) require.Nil(t, err) - channelMembers, err = ss.Group().ChannelMembersToAdd(0) + channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil) require.Nil(t, err) require.Empty(t, channelMembers) // restore group member and verify _, err = ss.Group().UpsertMember(group.Id, user.Id) require.Nil(t, err) - channelMembers, err = ss.Group().ChannelMembersToAdd(0) + channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil) require.Nil(t, err) require.Len(t, channelMembers, 1) // Adding Channel (ChannelMemberHistory) should stop returning result err = ss.ChannelMemberHistory().LogJoinEvent(user.Id, channel.Id, model.GetMillis()) require.Nil(t, err) - channelMembers, err = ss.Group().ChannelMembersToAdd(0) + channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil) require.Nil(t, err) require.Empty(t, channelMembers) // Leaving Channel (ChannelMemberHistory) should still not return result err = ss.ChannelMemberHistory().LogLeaveEvent(user.Id, channel.Id, model.GetMillis()) require.Nil(t, err) - channelMembers, err = ss.Group().ChannelMembersToAdd(0) + channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil) require.Nil(t, err) require.Empty(t, channelMembers) // Purging ChannelMemberHistory re-returns the result _, err = ss.ChannelMemberHistory().PermanentDeleteBatch(model.GetMillis()+1, 100) require.Nil(t, err) - channelMembers, err = ss.Group().ChannelMembersToAdd(0) + channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil) require.Nil(t, err) require.Len(t, channelMembers, 1) } -func testTeamMemberRemovals(t *testing.T, ss store.Store) { +func testChannelMembersToAddSingleChannel(t *testing.T, ss store.Store) { + group1, err := ss.Group().Create(&model.Group{ + Name: model.NewId(), + DisplayName: "TeamMembersToAdd Test Group", + RemoteId: model.NewId(), + Source: model.GroupSourceLdap, + }) + require.Nil(t, err) + + group2, err := ss.Group().Create(&model.Group{ + Name: model.NewId(), + DisplayName: "TeamMembersToAdd Test Group", + RemoteId: model.NewId(), + Source: model.GroupSourceLdap, + }) + require.Nil(t, err) + + user1 := &model.User{ + Email: MakeEmail(), + Username: model.NewId(), + } + user1, err = ss.User().Save(user1) + require.Nil(t, err) + + user2 := &model.User{ + Email: MakeEmail(), + Username: model.NewId(), + } + user2, err = ss.User().Save(user2) + require.Nil(t, err) + + user3 := &model.User{ + Email: MakeEmail(), + Username: model.NewId(), + } + user3, err = ss.User().Save(user3) + require.Nil(t, err) + + for _, user := range []*model.User{user1, user2} { + _, err = ss.Group().UpsertMember(group1.Id, user.Id) + require.Nil(t, err) + } + _, err = ss.Group().UpsertMember(group2.Id, user3.Id) + require.Nil(t, err) + + channel1 := &model.Channel{ + DisplayName: "Name", + Name: "z-z-" + model.NewId() + "a", + Type: model.CHANNEL_OPEN, + } + channel1, err = ss.Channel().Save(channel1, 999) + require.Nil(t, err) + + channel2 := &model.Channel{ + DisplayName: "Name", + Name: "z-z-" + model.NewId() + "a", + Type: model.CHANNEL_OPEN, + } + channel2, err = ss.Channel().Save(channel2, 999) + require.Nil(t, err) + + _, err = ss.Group().CreateGroupSyncable(model.NewGroupChannel(group1.Id, channel1.Id, true)) + require.Nil(t, err) + + _, err = ss.Group().CreateGroupSyncable(model.NewGroupChannel(group2.Id, channel2.Id, true)) + require.Nil(t, err) + + channelMembers, err := ss.Group().ChannelMembersToAdd(0, nil) + require.Nil(t, err) + require.GreaterOrEqual(t, len(channelMembers), 3) + + channelMembers, err = ss.Group().ChannelMembersToAdd(0, &channel1.Id) + require.Nil(t, err) + require.Len(t, channelMembers, 2) + + channelMembers, err = ss.Group().ChannelMembersToAdd(0, &channel2.Id) + require.Nil(t, err) + require.Len(t, channelMembers, 1) +} + +func testTeamMembersToRemove(t *testing.T, ss store.Store) { data := pendingMemberRemovalsDataSetup(t, ss) // one result when both users are in the group (for user C) - teamMembers, err := ss.Group().TeamMembersToRemove() + teamMembers, err := ss.Group().TeamMembersToRemove(nil) require.Nil(t, err) require.Len(t, teamMembers, 1) require.Equal(t, data.UserC.Id, teamMembers[0].UserId) @@ -1412,7 +1588,7 @@ func testTeamMemberRemovals(t *testing.T, ss store.Store) { require.Nil(t, err) // user b and c should now be returned - teamMembers, err = ss.Group().TeamMembersToRemove() + teamMembers, err = ss.Group().TeamMembersToRemove(nil) require.Nil(t, err) require.Len(t, teamMembers, 2) @@ -1428,12 +1604,12 @@ func testTeamMemberRemovals(t *testing.T, ss store.Store) { _, err = ss.Group().DeleteMember(data.Group.Id, data.UserA.Id) require.Nil(t, err) - teamMembers, err = ss.Group().TeamMembersToRemove() + teamMembers, err = ss.Group().TeamMembersToRemove(nil) require.Nil(t, err) require.Len(t, teamMembers, 3) // Make one of them a bot - teamMembers, err = ss.Group().TeamMembersToRemove() + teamMembers, err = ss.Group().TeamMembersToRemove(nil) require.Nil(t, err) teamMember := teamMembers[0] bot := &model.Bot{ @@ -1446,7 +1622,7 @@ func testTeamMemberRemovals(t *testing.T, ss store.Store) { require.Nil(t, err) // verify that bot is not returned in results - teamMembers, err = ss.Group().TeamMembersToRemove() + teamMembers, err = ss.Group().TeamMembersToRemove(nil) require.Nil(t, err) require.Len(t, teamMembers, 2) @@ -1455,7 +1631,7 @@ func testTeamMemberRemovals(t *testing.T, ss store.Store) { require.Nil(t, err) // Should be back to 3 users - teamMembers, err = ss.Group().TeamMembersToRemove() + teamMembers, err = ss.Group().TeamMembersToRemove(nil) require.Nil(t, err) require.Len(t, teamMembers, 3) @@ -1474,11 +1650,88 @@ func testTeamMemberRemovals(t *testing.T, ss store.Store) { require.Nil(t, err) } -func testChannelMemberRemovals(t *testing.T, ss store.Store) { +func testTeamMembersToRemoveSingleTeam(t *testing.T, ss store.Store) { + user1 := &model.User{ + Email: MakeEmail(), + Username: model.NewId(), + } + user1, err := ss.User().Save(user1) + require.Nil(t, err) + + user2 := &model.User{ + Email: MakeEmail(), + Username: model.NewId(), + } + user2, err = ss.User().Save(user2) + require.Nil(t, err) + + user3 := &model.User{ + Email: MakeEmail(), + Username: model.NewId(), + } + user3, err = ss.User().Save(user3) + require.Nil(t, err) + + team1 := &model.Team{ + DisplayName: "Name", + Description: "Some description", + CompanyName: "Some company name", + AllowOpenInvite: false, + InviteId: "inviteid0", + Name: "z-z-" + model.NewId() + "a", + Email: "success+" + model.NewId() + "@simulator.amazonses.com", + Type: model.TEAM_OPEN, + GroupConstrained: model.NewBool(true), + } + team1, err = ss.Team().Save(team1) + require.Nil(t, err) + + team2 := &model.Team{ + DisplayName: "Name", + Description: "Some description", + CompanyName: "Some company name", + AllowOpenInvite: false, + InviteId: "inviteid0", + Name: "z-z-" + model.NewId() + "a", + Email: "success+" + model.NewId() + "@simulator.amazonses.com", + Type: model.TEAM_OPEN, + GroupConstrained: model.NewBool(true), + } + team2, err = ss.Team().Save(team2) + require.Nil(t, err) + + for _, user := range []*model.User{user1, user2} { + _, err = ss.Team().SaveMember(&model.TeamMember{ + TeamId: team1.Id, + UserId: user.Id, + }, 999) + require.Nil(t, err) + } + + _, err = ss.Team().SaveMember(&model.TeamMember{ + TeamId: team2.Id, + UserId: user3.Id, + }, 999) + require.Nil(t, err) + + teamMembers, err := ss.Group().TeamMembersToRemove(nil) + require.Nil(t, err) + require.Len(t, teamMembers, 3) + + teamMembers, err = ss.Group().TeamMembersToRemove(&team1.Id) + require.Nil(t, err) + require.Len(t, teamMembers, 2) + + teamMembers, err = ss.Group().TeamMembersToRemove(&team2.Id) + require.Nil(t, err) + require.Len(t, teamMembers, 1) +} + +func testChannelMembersToRemove(t *testing.T, ss store.Store) { data := pendingMemberRemovalsDataSetup(t, ss) // one result when both users are in the group (for user C) - channelMembers, err := ss.Group().ChannelMembersToRemove() + channelMembers, err := ss.Group().ChannelMembersToRemove(nil) require.Nil(t, err) require.Len(t, channelMembers, 1) require.Equal(t, data.UserC.Id, channelMembers[0].UserId) @@ -1487,7 +1740,7 @@ func testChannelMemberRemovals(t *testing.T, ss store.Store) { require.Nil(t, err) // user b and c should now be returned - channelMembers, err = ss.Group().ChannelMembersToRemove() + channelMembers, err = ss.Group().ChannelMembersToRemove(nil) require.Nil(t, err) require.Len(t, channelMembers, 2) @@ -1503,12 +1756,12 @@ func testChannelMemberRemovals(t *testing.T, ss store.Store) { _, err = ss.Group().DeleteMember(data.Group.Id, data.UserA.Id) require.Nil(t, err) - channelMembers, err = ss.Group().ChannelMembersToRemove() + channelMembers, err = ss.Group().ChannelMembersToRemove(nil) require.Nil(t, err) require.Len(t, channelMembers, 3) // Make one of them a bot - channelMembers, err = ss.Group().ChannelMembersToRemove() + channelMembers, err = ss.Group().ChannelMembersToRemove(nil) require.Nil(t, err) channelMember := channelMembers[0] bot := &model.Bot{ @@ -1521,7 +1774,7 @@ func testChannelMemberRemovals(t *testing.T, ss store.Store) { require.Nil(t, err) // verify that bot is not returned in results - channelMembers, err = ss.Group().ChannelMembersToRemove() + channelMembers, err = ss.Group().ChannelMembersToRemove(nil) require.Nil(t, err) require.Len(t, channelMembers, 2) @@ -1530,7 +1783,7 @@ func testChannelMemberRemovals(t *testing.T, ss store.Store) { require.Nil(t, err) // Should be back to 3 users - channelMembers, err = ss.Group().ChannelMembersToRemove() + channelMembers, err = ss.Group().ChannelMembersToRemove(nil) require.Nil(t, err) require.Len(t, channelMembers, 3) @@ -1549,6 +1802,75 @@ func testChannelMemberRemovals(t *testing.T, ss store.Store) { require.Nil(t, err) } +func testChannelMembersToRemoveSingleChannel(t *testing.T, ss store.Store) { + user1 := &model.User{ + Email: MakeEmail(), + Username: model.NewId(), + } + user1, err := ss.User().Save(user1) + require.Nil(t, err) + + user2 := &model.User{ + Email: MakeEmail(), + Username: model.NewId(), + } + user2, err = ss.User().Save(user2) + require.Nil(t, err) + + user3 := &model.User{ + Email: MakeEmail(), + Username: model.NewId(), + } + user3, err = ss.User().Save(user3) + require.Nil(t, err) + + channel1 := &model.Channel{ + DisplayName: "Name", + Name: "z-z-" + model.NewId() + "a", + Type: model.CHANNEL_OPEN, + GroupConstrained: model.NewBool(true), + } + channel1, err = ss.Channel().Save(channel1, 999) + require.Nil(t, err) + + channel2 := &model.Channel{ + DisplayName: "Name", + Name: "z-z-" + model.NewId() + "a", + Type: model.CHANNEL_OPEN, + GroupConstrained: model.NewBool(true), + } + channel2, err = ss.Channel().Save(channel2, 999) + require.Nil(t, err) + + for _, user := range []*model.User{user1, user2} { + _, err = ss.Channel().SaveMember(&model.ChannelMember{ + ChannelId: channel1.Id, + UserId: user.Id, + NotifyProps: model.GetDefaultChannelNotifyProps(), + }) + require.Nil(t, err) + } + + _, err = ss.Channel().SaveMember(&model.ChannelMember{ + ChannelId: channel2.Id, + UserId: user3.Id, + NotifyProps: model.GetDefaultChannelNotifyProps(), + }) + require.Nil(t, err) + + channelMembers, err := ss.Group().ChannelMembersToRemove(nil) + require.Nil(t, err) + require.Len(t, channelMembers, 3) + + channelMembers, err = ss.Group().ChannelMembersToRemove(&channel1.Id) + require.Nil(t, err) + require.Len(t, channelMembers, 2) + + channelMembers, err = ss.Group().ChannelMembersToRemove(&channel2.Id) + require.Nil(t, err) + require.Len(t, channelMembers, 1) +} + type removalsData struct { UserA *model.User UserB *model.User @@ -1730,7 +2052,6 @@ func testGetGroupsByChannel(t *testing.T, ss store.Store) { DisplayName: "group-1", RemoteId: model.NewId(), Source: model.GroupSourceLdap, - // SchemeAdmin: model.NewBool(false), }) require.Nil(t, err) @@ -1739,7 +2060,6 @@ func testGetGroupsByChannel(t *testing.T, ss store.Store) { DisplayName: "group-2", RemoteId: model.NewId(), Source: model.GroupSourceLdap, - // SchemeAdmin: model.NewBool(false), }) require.Nil(t, err) @@ -1770,7 +2090,6 @@ func testGetGroupsByChannel(t *testing.T, ss store.Store) { DisplayName: "group-3", RemoteId: model.NewId(), Source: model.GroupSourceLdap, - // SchemeAdmin: model.NewBool(false), }) require.Nil(t, err) diff --git a/store/storetest/mocks/ChannelStore.go b/store/storetest/mocks/ChannelStore.go index a341229311..d28537fc71 100644 --- a/store/storetest/mocks/ChannelStore.go +++ b/store/storetest/mocks/ChannelStore.go @@ -1173,16 +1173,6 @@ func (_m *ChannelStore) InvalidateMemberCount(channelId string) { _m.Called(channelId) } -// InvalidateMembersForAllUsers provides a mock function with given fields: -func (_m *ChannelStore) InvalidateMembersForAllUsers() { - _m.Called() -} - -// InvalidateMembersForUser provides a mock function with given fields: userId -func (_m *ChannelStore) InvalidateMembersForUser(userId string) { - _m.Called(userId) -} - // InvalidatePinnedPostCount provides a mock function with given fields: channelId func (_m *ChannelStore) InvalidatePinnedPostCount(channelId string) { _m.Called(channelId) diff --git a/store/storetest/mocks/GroupStore.go b/store/storetest/mocks/GroupStore.go index 30055d39da..cc618e88bb 100644 --- a/store/storetest/mocks/GroupStore.go +++ b/store/storetest/mocks/GroupStore.go @@ -64,13 +64,13 @@ func (_m *GroupStore) ChannelMembersMinusGroupMembers(channelID string, groupIDs return r0, r1 } -// ChannelMembersToAdd provides a mock function with given fields: since -func (_m *GroupStore) ChannelMembersToAdd(since int64) ([]*model.UserChannelIDPair, *model.AppError) { - ret := _m.Called(since) +// ChannelMembersToAdd provides a mock function with given fields: since, channelID +func (_m *GroupStore) ChannelMembersToAdd(since int64, channelID *string) ([]*model.UserChannelIDPair, *model.AppError) { + ret := _m.Called(since, channelID) var r0 []*model.UserChannelIDPair - if rf, ok := ret.Get(0).(func(int64) []*model.UserChannelIDPair); ok { - r0 = rf(since) + if rf, ok := ret.Get(0).(func(int64, *string) []*model.UserChannelIDPair); ok { + r0 = rf(since, channelID) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*model.UserChannelIDPair) @@ -78,8 +78,8 @@ func (_m *GroupStore) ChannelMembersToAdd(since int64) ([]*model.UserChannelIDPa } var r1 *model.AppError - if rf, ok := ret.Get(1).(func(int64) *model.AppError); ok { - r1 = rf(since) + if rf, ok := ret.Get(1).(func(int64, *string) *model.AppError); ok { + r1 = rf(since, channelID) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*model.AppError) @@ -89,13 +89,13 @@ func (_m *GroupStore) ChannelMembersToAdd(since int64) ([]*model.UserChannelIDPa return r0, r1 } -// ChannelMembersToRemove provides a mock function with given fields: -func (_m *GroupStore) ChannelMembersToRemove() ([]*model.ChannelMember, *model.AppError) { - ret := _m.Called() +// ChannelMembersToRemove provides a mock function with given fields: channelID +func (_m *GroupStore) ChannelMembersToRemove(channelID *string) ([]*model.ChannelMember, *model.AppError) { + ret := _m.Called(channelID) var r0 []*model.ChannelMember - if rf, ok := ret.Get(0).(func() []*model.ChannelMember); ok { - r0 = rf() + if rf, ok := ret.Get(0).(func(*string) []*model.ChannelMember); ok { + r0 = rf(channelID) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*model.ChannelMember) @@ -103,8 +103,8 @@ func (_m *GroupStore) ChannelMembersToRemove() ([]*model.ChannelMember, *model.A } var r1 *model.AppError - if rf, ok := ret.Get(1).(func() *model.AppError); ok { - r1 = rf() + if rf, ok := ret.Get(1).(func(*string) *model.AppError); ok { + r1 = rf(channelID) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*model.AppError) @@ -745,13 +745,13 @@ func (_m *GroupStore) TeamMembersMinusGroupMembers(teamID string, groupIDs []str return r0, r1 } -// TeamMembersToAdd provides a mock function with given fields: since -func (_m *GroupStore) TeamMembersToAdd(since int64) ([]*model.UserTeamIDPair, *model.AppError) { - ret := _m.Called(since) +// TeamMembersToAdd provides a mock function with given fields: since, teamID +func (_m *GroupStore) TeamMembersToAdd(since int64, teamID *string) ([]*model.UserTeamIDPair, *model.AppError) { + ret := _m.Called(since, teamID) var r0 []*model.UserTeamIDPair - if rf, ok := ret.Get(0).(func(int64) []*model.UserTeamIDPair); ok { - r0 = rf(since) + if rf, ok := ret.Get(0).(func(int64, *string) []*model.UserTeamIDPair); ok { + r0 = rf(since, teamID) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*model.UserTeamIDPair) @@ -759,8 +759,8 @@ func (_m *GroupStore) TeamMembersToAdd(since int64) ([]*model.UserTeamIDPair, *m } var r1 *model.AppError - if rf, ok := ret.Get(1).(func(int64) *model.AppError); ok { - r1 = rf(since) + if rf, ok := ret.Get(1).(func(int64, *string) *model.AppError); ok { + r1 = rf(since, teamID) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*model.AppError) @@ -770,13 +770,13 @@ func (_m *GroupStore) TeamMembersToAdd(since int64) ([]*model.UserTeamIDPair, *m return r0, r1 } -// TeamMembersToRemove provides a mock function with given fields: -func (_m *GroupStore) TeamMembersToRemove() ([]*model.TeamMember, *model.AppError) { - ret := _m.Called() +// TeamMembersToRemove provides a mock function with given fields: teamID +func (_m *GroupStore) TeamMembersToRemove(teamID *string) ([]*model.TeamMember, *model.AppError) { + ret := _m.Called(teamID) var r0 []*model.TeamMember - if rf, ok := ret.Get(0).(func() []*model.TeamMember); ok { - r0 = rf() + if rf, ok := ret.Get(0).(func(*string) []*model.TeamMember); ok { + r0 = rf(teamID) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*model.TeamMember) @@ -784,8 +784,8 @@ func (_m *GroupStore) TeamMembersToRemove() ([]*model.TeamMember, *model.AppErro } var r1 *model.AppError - if rf, ok := ret.Get(1).(func() *model.AppError); ok { - r1 = rf() + if rf, ok := ret.Get(1).(func(*string) *model.AppError); ok { + r1 = rf(teamID) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*model.AppError) diff --git a/store/timer_layer.go b/store/timer_layer.go index 45f30c2781..ad1f4cffe9 100644 --- a/store/timer_layer.go +++ b/store/timer_layer.go @@ -2557,10 +2557,10 @@ func (s *TimerLayerGroupStore) ChannelMembersMinusGroupMembers(channelID string, return resultVar0, resultVar1 } -func (s *TimerLayerGroupStore) ChannelMembersToAdd(since int64) ([]*model.UserChannelIDPair, *model.AppError) { +func (s *TimerLayerGroupStore) ChannelMembersToAdd(since int64, channelID *string) ([]*model.UserChannelIDPair, *model.AppError) { start := timemodule.Now() - resultVar0, resultVar1 := s.GroupStore.ChannelMembersToAdd(since) + resultVar0, resultVar1 := s.GroupStore.ChannelMembersToAdd(since, channelID) elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second) if s.Root.Metrics != nil { @@ -2573,10 +2573,10 @@ func (s *TimerLayerGroupStore) ChannelMembersToAdd(since int64) ([]*model.UserCh return resultVar0, resultVar1 } -func (s *TimerLayerGroupStore) ChannelMembersToRemove() ([]*model.ChannelMember, *model.AppError) { +func (s *TimerLayerGroupStore) ChannelMembersToRemove(channelID *string) ([]*model.ChannelMember, *model.AppError) { start := timemodule.Now() - resultVar0, resultVar1 := s.GroupStore.ChannelMembersToRemove() + resultVar0, resultVar1 := s.GroupStore.ChannelMembersToRemove(channelID) elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second) if s.Root.Metrics != nil { @@ -3005,10 +3005,10 @@ func (s *TimerLayerGroupStore) TeamMembersMinusGroupMembers(teamID string, group return resultVar0, resultVar1 } -func (s *TimerLayerGroupStore) TeamMembersToAdd(since int64) ([]*model.UserTeamIDPair, *model.AppError) { +func (s *TimerLayerGroupStore) TeamMembersToAdd(since int64, teamID *string) ([]*model.UserTeamIDPair, *model.AppError) { start := timemodule.Now() - resultVar0, resultVar1 := s.GroupStore.TeamMembersToAdd(since) + resultVar0, resultVar1 := s.GroupStore.TeamMembersToAdd(since, teamID) elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second) if s.Root.Metrics != nil { @@ -3021,10 +3021,10 @@ func (s *TimerLayerGroupStore) TeamMembersToAdd(since int64) ([]*model.UserTeamI return resultVar0, resultVar1 } -func (s *TimerLayerGroupStore) TeamMembersToRemove() ([]*model.TeamMember, *model.AppError) { +func (s *TimerLayerGroupStore) TeamMembersToRemove(teamID *string) ([]*model.TeamMember, *model.AppError) { start := timemodule.Now() - resultVar0, resultVar1 := s.GroupStore.TeamMembersToRemove() + resultVar0, resultVar1 := s.GroupStore.TeamMembersToRemove(teamID) elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second) if s.Root.Metrics != nil {