From a6d8e4529762dcc63e5db6f7e67f6e1a873b7743 Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Wed, 11 May 2022 13:54:12 +0530 Subject: [PATCH] MM-44088: Add teamID filter to channelMembers (#20176) We add 2 new params to channel members query. 1. Filter by teamId. 2. Negate that filter. We include some more optimizations like: - Moved the team role checks inside the dataloader. - Moved the channel pretty name computation inside the loader. Now that we load less data on initial load, we can reduce the concurrency requirement to be a bit on the safer side. ```release-note NONE ``` --- api4/graphql.go | 2 +- api4/resolver.go | 27 ++++++- api4/resolver_channel_member.go | 37 +++++---- api4/resolver_channel_member_test.go | 39 ++++++++++ api4/resolver_team.go | 26 +++---- api4/schema.graphqls | 2 + store/layer_generators/main.go | 8 +- store/opentracinglayer/opentracinglayer.go | 4 +- store/retrylayer/retrylayer.go | 4 +- store/sqlstore/channel_store.go | 32 ++++++-- store/store.go | 12 ++- store/storetest/channel_store.go | 89 ++++++++++++++++++---- store/storetest/mocks/ChannelStore.go | 14 ++-- store/timerlayer/timerlayer.go | 4 +- 14 files changed, 225 insertions(+), 75 deletions(-) diff --git a/api4/graphql.go b/api4/graphql.go index 964dce2c05..11e3b55262 100644 --- a/api4/graphql.go +++ b/api4/graphql.go @@ -34,7 +34,7 @@ const ( usersLoaderCtx ctxKey = 4 ) -const loaderBatchCapacity = web.PerPageMaximum + 100 +const loaderBatchCapacity = web.PerPageMaximum //go:embed schema.graphqls var schemaRaw string diff --git a/api4/resolver.go b/api4/resolver.go index 81633967e8..15c99ce813 100644 --- a/api4/resolver.go +++ b/api4/resolver.go @@ -11,6 +11,7 @@ import ( "github.com/graph-gophers/dataloader/v6" "github.com/mattermost/mattermost-server/v6/app" "github.com/mattermost/mattermost-server/v6/model" + "github.com/mattermost/mattermost-server/v6/store" "github.com/mattermost/mattermost-server/v6/web" ) @@ -211,7 +212,9 @@ func (*resolver) ChannelsLeft(ctx context.Context, args struct { // match with api4.getChannelMember func (*resolver) ChannelMembers(ctx context.Context, args struct { UserID string + TeamID string ChannelID string + ExcludeTeam bool First int32 After string LastUpdateAt float64 @@ -263,7 +266,29 @@ func (*resolver) ChannelMembers(ctx context.Context, args struct { } } - members, err := c.App.Srv().Store.Channel().GetMembersForUserWithCursor(args.UserID, afterChannel, afterUser, limit, int(args.LastUpdateAt)) + if args.TeamID != "" { + if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), args.TeamID, model.PermissionViewTeam) { + primaryTeam := *c.App.Config().TeamSettings.ExperimentalPrimaryTeam + if primaryTeam != "" { + team, appErr := c.App.GetTeamByName(primaryTeam) + if appErr != nil { + return []*channelMember{}, appErr + } + args.TeamID = team.Id + } else { + return []*channelMember{}, nil + } + } + } + + opts := &store.ChannelMemberGraphQLSearchOpts{ + AfterChannel: afterChannel, + AfterUser: afterUser, + Limit: limit, + LastUpdateAt: int(args.LastUpdateAt), + ExcludeTeam: args.ExcludeTeam, + } + members, err := c.App.Srv().Store.Channel().GetMembersForUserWithCursor(args.UserID, args.TeamID, opts) if err != nil { return nil, err } diff --git a/api4/resolver_channel_member.go b/api4/resolver_channel_member.go index 86bb1bbb18..32329f7f4b 100644 --- a/api4/resolver_channel_member.go +++ b/api4/resolver_channel_member.go @@ -41,7 +41,7 @@ func (cm *channelMember) Channel(ctx context.Context) (*channel, error) { if err != nil { return nil, err } - channel := result.(*model.Channel) + channel := result.(*channel) if channel.Type == model.ChannelTypeOpen { if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PermissionReadPublicChannel) && @@ -56,20 +56,7 @@ func (cm *channelMember) Channel(ctx context.Context) (*channel, error) { } } - appErr := c.App.FillInChannelProps(channel) - if appErr != nil { - return nil, appErr - } - - res, err := postProcessChannels(c, []*model.Channel{channel}) - if err != nil { - return nil, err - } - // A bit of defence-in-depth; can probably be removed after a deeper look. - if len(res) != 1 { - return nil, fmt.Errorf("postProcessChannels: incorrect number of channels returned %d", len(res)) - } - return res[0], nil + return channel, nil } func graphQLChannelsLoader(ctx context.Context, keys dataloader.Keys) []*dataloader.Result { @@ -98,7 +85,7 @@ func graphQLChannelsLoader(ctx context.Context, keys dataloader.Keys) []*dataloa return result } -func getGraphQLChannels(c *web.Context, channelIDs []string) ([]*model.Channel, error) { +func getGraphQLChannels(c *web.Context, channelIDs []string) ([]*channel, error) { channels, appErr := c.App.GetChannels(channelIDs) if appErr != nil { return nil, appErr @@ -108,18 +95,28 @@ func getGraphQLChannels(c *web.Context, channelIDs []string) ([]*model.Channel, return nil, fmt.Errorf("all channels were not found. Requested %d; Found %d", len(channelIDs), len(channels)) } + appErr = c.App.FillInChannelsProps(model.ChannelList(channels)) + if appErr != nil { + return nil, appErr + } + + res, err := postProcessChannels(c, channels) + if err != nil { + return nil, err + } + // The channels need to be in the exact same order as the input slice. - tmp := make(map[string]*model.Channel) - for _, ch := range channels { + tmp := make(map[string]*channel) + for _, ch := range res { tmp[ch.Id] = ch } // We reuse the same slice and just rewrite the channels. for i, id := range channelIDs { - channels[i] = tmp[id] + res[i] = tmp[id] } - return channels, nil + return res, nil } func (cm *channelMember) Roles_(ctx context.Context) ([]*model.Role, error) { diff --git a/api4/resolver_channel_member_test.go b/api4/resolver_channel_member_test.go index 848ad32e54..a4b622d5e2 100644 --- a/api4/resolver_channel_member_test.go +++ b/api4/resolver_channel_member_test.go @@ -292,6 +292,45 @@ func TestGraphQLChannelMembers(t *testing.T) { require.Len(t, resp.Errors, 1) }) + t.Run("team_filter", func(t *testing.T) { + query := `query channelMembers($teamId: String, $excludeTeam: Boolean = false) { + channelMembers(userId: "me", teamId: $teamId, excludeTeam: $excludeTeam) { + channel { + id + } + } + } + ` + input := graphQLInput{ + OperationName: "channelMembers", + Query: query, + Variables: map[string]interface{}{ + "teamId": th.BasicTeam.Id, + }, + } + + 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.ChannelMembers, 5) + + input = graphQLInput{ + OperationName: "channelMembers", + Query: query, + Variables: map[string]interface{}{ + "teamId": th.BasicTeam.Id, + "excludeTeam": true, + }, + } + + 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.ChannelMembers, 4) + }) + t.Run("UpdateAt", func(t *testing.T) { query := `query channelMembers($first: Int, $after: String = "", $lastUpdateAt: Float) { channelMembers(userId: "me", first: $first, after: $after, lastUpdateAt: $lastUpdateAt) { diff --git a/api4/resolver_team.go b/api4/resolver_team.go index be46bbed89..de122807de 100644 --- a/api4/resolver_team.go +++ b/api4/resolver_team.go @@ -14,11 +14,6 @@ import ( ) func getGraphQLTeam(ctx context.Context, id string) (*model.Team, error) { - c, err := getCtx(ctx) - if err != nil { - return nil, err - } - loader, err := getTeamsLoader(ctx) if err != nil { return nil, err @@ -30,15 +25,6 @@ func getGraphQLTeam(ctx context.Context, id string) (*model.Team, error) { return nil, err } team := result.(*model.Team) - team = team.ShallowCopy() - - if (!team.AllowOpenInvite || team.Type != model.TeamOpen) && - !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), team.Id, model.PermissionViewTeam) { - c.SetPermissionError(model.PermissionViewTeam) - return nil, c.Err - } - - team = c.App.SanitizeTeam(*c.AppContext.Session(), team) return team, nil } @@ -78,6 +64,18 @@ func getGraphQLTeams(c *web.Context, teamIDs []string) ([]*model.Team, error) { return nil, fmt.Errorf("All teams were not found. Requested %d; Found %d", len(teamIDs), len(teams)) } + // We pre-calculate this so that it's not computed in separate goroutines outside + // the dataloader. + for i := range teams { + if (!teams[i].AllowOpenInvite || teams[i].Type != model.TeamOpen) && + !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teams[i].Id, model.PermissionViewTeam) { + c.SetPermissionError(model.PermissionViewTeam) + return nil, c.Err + } + + teams[i] = c.App.SanitizeTeam(*c.AppContext.Session(), teams[i]) + } + // The teams need to be in the exact same order as the input slice. tmp := make(map[string]*model.Team) for _, ch := range teams { diff --git a/api4/schema.graphqls b/api4/schema.graphqls index 39e91574fd..9085dd2e21 100644 --- a/api4/schema.graphqls +++ b/api4/schema.graphqls @@ -19,6 +19,8 @@ type Query { since: Float!): [String!]! channelMembers(userId: String!, channelId: String = "", + teamId: String = "", + excludeTeam: Boolean = false, first: Int = 60, after: String = "", lastUpdateAt: Float = 0): [ChannelMember]! diff --git a/store/layer_generators/main.go b/store/layer_generators/main.go index fcbe9783ca..f630dca1d9 100644 --- a/store/layer_generators/main.go +++ b/store/layer_generators/main.go @@ -284,8 +284,8 @@ func generateLayer(name, templateFile string) ([]byte, error) { switch param.Type { case "ChannelSearchOpts", "UserGetByIdsOpts", "ThreadMembershipOpts": paramsWithType = append(paramsWithType, fmt.Sprintf("%s store.%s", param.Name, param.Type)) - case "*UserGetByIdsOpts": - paramsWithType = append(paramsWithType, fmt.Sprintf("%s *store.UserGetByIdsOpts", param.Name)) + case "*UserGetByIdsOpts", "*ChannelMemberGraphQLSearchOpts": + paramsWithType = append(paramsWithType, fmt.Sprintf("%s *store.%s", param.Name, strings.TrimPrefix(param.Type, "*"))) default: paramsWithType = append(paramsWithType, fmt.Sprintf("%s %s", param.Name, param.Type)) } @@ -298,8 +298,8 @@ func generateLayer(name, templateFile string) ([]byte, error) { switch param.Type { case "ChannelSearchOpts", "UserGetByIdsOpts", "ThreadMembershipOpts": paramsWithType = append(paramsWithType, fmt.Sprintf("%s store.%s", param.Name, param.Type)) - case "*UserGetByIdsOpts": - paramsWithType = append(paramsWithType, fmt.Sprintf("%s *store.UserGetByIdsOpts", param.Name)) + case "*UserGetByIdsOpts", "*ChannelMemberGraphQLSearchOpts": + paramsWithType = append(paramsWithType, fmt.Sprintf("%s *store.%s", param.Name, strings.TrimPrefix(param.Type, "*"))) default: paramsWithType = append(paramsWithType, fmt.Sprintf("%s %s", param.Name, param.Type)) } diff --git a/store/opentracinglayer/opentracinglayer.go b/store/opentracinglayer/opentracinglayer.go index f5c9e0bb92..b37b14073b 100644 --- a/store/opentracinglayer/opentracinglayer.go +++ b/store/opentracinglayer/opentracinglayer.go @@ -1478,7 +1478,7 @@ func (s *OpenTracingLayerChannelStore) GetMembersForUser(teamID string, userID s return result, err } -func (s *OpenTracingLayerChannelStore) GetMembersForUserWithCursor(userID string, afterChannel string, afterUser string, limit int, lastUpdateAt int) (model.ChannelMembers, error) { +func (s *OpenTracingLayerChannelStore) GetMembersForUserWithCursor(userID string, teamID string, opts *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) { origCtx := s.Root.Store.Context() span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetMembersForUserWithCursor") s.Root.Store.SetContext(newCtx) @@ -1487,7 +1487,7 @@ func (s *OpenTracingLayerChannelStore) GetMembersForUserWithCursor(userID string }() defer span.Finish() - result, err := s.ChannelStore.GetMembersForUserWithCursor(userID, afterChannel, afterUser, limit, lastUpdateAt) + result, err := s.ChannelStore.GetMembersForUserWithCursor(userID, teamID, opts) 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 c57f6de2be..84b684e55c 100644 --- a/store/retrylayer/retrylayer.go +++ b/store/retrylayer/retrylayer.go @@ -1660,11 +1660,11 @@ func (s *RetryLayerChannelStore) GetMembersForUser(teamID string, userID string) } -func (s *RetryLayerChannelStore) GetMembersForUserWithCursor(userID string, afterChannel string, afterUser string, limit int, lastUpdateAt int) (model.ChannelMembers, error) { +func (s *RetryLayerChannelStore) GetMembersForUserWithCursor(userID string, teamID string, opts *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) { tries := 0 for { - result, err := s.ChannelStore.GetMembersForUserWithCursor(userID, afterChannel, afterUser, limit, lastUpdateAt) + result, err := s.ChannelStore.GetMembersForUserWithCursor(userID, teamID, opts) if err == nil { return result, nil } diff --git a/store/sqlstore/channel_store.go b/store/sqlstore/channel_store.go index c97fd3075e..b501749138 100644 --- a/store/sqlstore/channel_store.go +++ b/store/sqlstore/channel_store.go @@ -2814,7 +2814,7 @@ func (s SqlChannelStore) GetMembersForUser(teamID string, userID string) (model. return dbMembers.ToModel(), nil } -func (s SqlChannelStore) GetMembersForUserWithCursor(userID, afterChannel, afterUser string, limit, lastUpdateAt int) (model.ChannelMembers, error) { +func (s SqlChannelStore) GetMembersForUserWithCursor(userID, teamID string, opts *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) { query := s.getQueryBuilder(). Select("ChannelMembers.*", "TeamScheme.DefaultChannelGuestRole TeamSchemeDefaultGuestRole", @@ -2834,20 +2834,36 @@ func (s SqlChannelStore) GetMembersForUserWithCursor(userID, afterChannel, after }). OrderBy("ChannelId, UserId ASC"). // The limit is verified at the GraphQL layer. - Limit(uint64(limit)) + Limit(uint64(opts.Limit)) - if afterChannel != "" && afterUser != "" { + if teamID != "" { + if opts.ExcludeTeam { + // Exclude this team and DM/GMs + query = query.Where(sq.And{ + sq.NotEq{"Channels.TeamId": teamID}, + sq.NotEq{"Channels.TeamId": ""}, + }) + } else { + // Include this team and DM/GMs + query = query.Where(sq.Or{ + sq.Eq{"Channels.TeamId": teamID}, + sq.Eq{"Channels.TeamId": ""}, + }) + } + } + + if opts.AfterChannel != "" && opts.AfterUser != "" { query = query.Where(sq.Or{ - sq.Gt{"ChannelMembers.ChannelId": afterChannel}, + sq.Gt{"ChannelMembers.ChannelId": opts.AfterChannel}, sq.And{ - sq.Eq{"ChannelMembers.ChannelId": afterChannel}, - sq.Gt{"ChannelMembers.UserId": afterUser}, + sq.Eq{"ChannelMembers.ChannelId": opts.AfterChannel}, + sq.Gt{"ChannelMembers.UserId": opts.AfterUser}, }, }) } - if lastUpdateAt != 0 { - query = query.Where(sq.GtOrEq{"ChannelMembers.LastUpdateAt": lastUpdateAt}) + if opts.LastUpdateAt != 0 { + query = query.Where(sq.GtOrEq{"ChannelMembers.LastUpdateAt": opts.LastUpdateAt}) } queryString, args, err := query.ToSql() diff --git a/store/store.go b/store/store.go index e0a82154cf..3b6fedf28a 100644 --- a/store/store.go +++ b/store/store.go @@ -234,7 +234,7 @@ type ChannelStore interface { GetMembersForUser(teamID string, userID string) (model.ChannelMembers, error) GetTeamMembersForChannel(channelID string) ([]string, error) GetMembersForUserWithPagination(userID string, page, perPage int) (model.ChannelMembersWithTeamData, error) - GetMembersForUserWithCursor(userID, afterChannel, afterUser string, limit, lastUpdateAt int) (model.ChannelMembers, error) + GetMembersForUserWithCursor(userID, teamID string, opts *ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) Autocomplete(userID, term string, includeDeleted bool) (model.ChannelListWithTeamData, error) AutocompleteInTeam(teamID, userID, term string, includeDeleted bool) (model.ChannelList, error) AutocompleteInTeamForSearch(teamID string, userID string, term string, includeDeleted bool) (model.ChannelList, error) @@ -983,3 +983,13 @@ type ThreadMembershipOpts struct { // should be updated. UpdateParticipants bool } + +// ChannelMemberGraphQLSearchOpts contains the options for a graphQL query +// to get the channel members. +type ChannelMemberGraphQLSearchOpts struct { + AfterChannel string + AfterUser string + Limit int + LastUpdateAt int + ExcludeTeam bool +} diff --git a/store/storetest/channel_store.go b/store/storetest/channel_store.go index 6860a651f4..8d7843a615 100644 --- a/store/storetest/channel_store.go +++ b/store/storetest/channel_store.go @@ -4438,6 +4438,14 @@ func testChannelStoreGetMembersForUserWithCursor(t *testing.T, ss store.Store) { _, err := ss.Team().Save(&t1) require.NoError(t, err) + t2 := model.Team{} + t2.DisplayName = "Team2" + t2.Name = NewTestId() + t2.Email = MakeEmail() + t2.Type = model.TeamOpen + _, err = ss.Team().Save(&t2) + require.NoError(t, err) + o1 := model.Channel{} o1.TeamId = t1.Id o1.DisplayName = "Channel1" @@ -4454,6 +4462,14 @@ func testChannelStoreGetMembersForUserWithCursor(t *testing.T, ss store.Store) { _, nErr = ss.Channel().Save(&o2, -1) require.NoError(t, nErr) + o3 := model.Channel{} + o3.TeamId = t2.Id + o3.DisplayName = "Channel3" + o3.Name = NewTestId() + o3.Type = model.ChannelTypeOpen + _, nErr = ss.Channel().Save(&o3, -1) + require.NoError(t, nErr) + m1 := model.ChannelMember{} m1.ChannelId = o1.Id m1.UserId = model.NewId() @@ -4468,15 +4484,29 @@ func testChannelStoreGetMembersForUserWithCursor(t *testing.T, ss store.Store) { _, err = ss.Channel().SaveMember(&m2) require.NoError(t, err) + m3 := model.ChannelMember{} + m3.ChannelId = o3.Id + m3.UserId = m1.UserId + m3.NotifyProps = model.GetDefaultChannelNotifyProps() + _, err = ss.Channel().SaveMember(&m3) + require.NoError(t, err) + t.Run("with channels", func(t *testing.T) { var members model.ChannelMembers - members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", "", 1, 0) + opts := &store.ChannelMemberGraphQLSearchOpts{ + Limit: 1, + } + members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts) require.NoError(t, err) assert.Len(t, members, 1) - members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", "", 3, 0) + opts.Limit = 3 + members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts) require.NoError(t, err) - assert.Len(t, members, 2) - members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, members[0].ChannelId, m1.UserId, 1, 0) + assert.Len(t, members, 3) + opts.AfterChannel = members[0].ChannelId + opts.AfterUser = m1.UserId + opts.Limit = 1 + members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts) require.NoError(t, err) assert.Len(t, members, 1) }) @@ -4495,15 +4525,41 @@ func testChannelStoreGetMembersForUserWithCursor(t *testing.T, ss store.Store) { _, nErr = ss.Channel().CreateDirectChannel(&u3, &u4) require.NoError(t, nErr) - members, err2 := ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", "", 10, 0) + opts := &store.ChannelMemberGraphQLSearchOpts{ + Limit: 10, + } + members, err2 := ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts) require.NoError(t, err2) - assert.Len(t, members, 4) + assert.Len(t, members, 5) - members, err2 = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", "", 2, 0) + opts.Limit = 2 + members, err2 = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts) require.NoError(t, err2) assert.Len(t, members, 2) - members, err2 = ss.Channel().GetMembersForUserWithCursor(m1.UserId, members[1].ChannelId, m1.UserId, 2, 0) + opts.AfterChannel = members[1].ChannelId + opts.AfterUser = m1.UserId + opts.Limit = 2 + members, err2 = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts) + require.NoError(t, err2) + assert.Len(t, members, 2) + }) + + t.Run("for a specific team", func(t *testing.T) { + opts := &store.ChannelMemberGraphQLSearchOpts{ + Limit: 10, + } + members, err2 := ss.Channel().GetMembersForUserWithCursor(m1.UserId, t2.Id, opts) + require.NoError(t, err2) + assert.Len(t, members, 3) + }) + + t.Run("excluding a team", func(t *testing.T) { + opts := &store.ChannelMemberGraphQLSearchOpts{ + Limit: 10, + ExcludeTeam: true, + } + members, err2 := ss.Channel().GetMembersForUserWithCursor(m1.UserId, t2.Id, opts) require.NoError(t, err2) assert.Len(t, members, 2) }) @@ -4529,17 +4585,24 @@ func testChannelStoreGetMembersForUserWithCursor(t *testing.T, ss store.Store) { _, err = ss.Channel().SaveMember(cm) require.NoError(t, err) } - members, err := ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", "", 10, 0) + opts := &store.ChannelMemberGraphQLSearchOpts{ + Limit: 10, + } + members, err := ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts) require.NoError(t, err) - assert.Len(t, members, 5) + assert.Len(t, members, 6) - members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", "", 2, 0) + opts.Limit = 2 + members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts) require.NoError(t, err) assert.Len(t, members, 2) - members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, members[1].ChannelId, m1.UserId, 10, 0) + opts.AfterChannel = members[1].ChannelId + opts.AfterUser = m1.UserId + opts.Limit = 10 + members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts) require.NoError(t, err) - assert.Len(t, members, 3) + assert.Len(t, members, 4) }) } diff --git a/store/storetest/mocks/ChannelStore.go b/store/storetest/mocks/ChannelStore.go index 6b1a0e350c..d990481ee5 100644 --- a/store/storetest/mocks/ChannelStore.go +++ b/store/storetest/mocks/ChannelStore.go @@ -1165,13 +1165,13 @@ func (_m *ChannelStore) GetMembersForUser(teamID string, userID string) (model.C return r0, r1 } -// GetMembersForUserWithCursor provides a mock function with given fields: userID, afterChannel, afterUser, limit, lastUpdateAt -func (_m *ChannelStore) GetMembersForUserWithCursor(userID string, afterChannel string, afterUser string, limit int, lastUpdateAt int) (model.ChannelMembers, error) { - ret := _m.Called(userID, afterChannel, afterUser, limit, lastUpdateAt) +// GetMembersForUserWithCursor provides a mock function with given fields: userID, teamID, opts +func (_m *ChannelStore) GetMembersForUserWithCursor(userID string, teamID string, opts *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) { + ret := _m.Called(userID, teamID, opts) var r0 model.ChannelMembers - if rf, ok := ret.Get(0).(func(string, string, string, int, int) model.ChannelMembers); ok { - r0 = rf(userID, afterChannel, afterUser, limit, lastUpdateAt) + if rf, ok := ret.Get(0).(func(string, string, *store.ChannelMemberGraphQLSearchOpts) model.ChannelMembers); ok { + r0 = rf(userID, teamID, opts) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(model.ChannelMembers) @@ -1179,8 +1179,8 @@ func (_m *ChannelStore) GetMembersForUserWithCursor(userID string, afterChannel } var r1 error - if rf, ok := ret.Get(1).(func(string, string, string, int, int) error); ok { - r1 = rf(userID, afterChannel, afterUser, limit, lastUpdateAt) + if rf, ok := ret.Get(1).(func(string, string, *store.ChannelMemberGraphQLSearchOpts) error); ok { + r1 = rf(userID, teamID, opts) } else { r1 = ret.Error(1) } diff --git a/store/timerlayer/timerlayer.go b/store/timerlayer/timerlayer.go index 0e4879658d..0e1cdddb12 100644 --- a/store/timerlayer/timerlayer.go +++ b/store/timerlayer/timerlayer.go @@ -1365,10 +1365,10 @@ func (s *TimerLayerChannelStore) GetMembersForUser(teamID string, userID string) return result, err } -func (s *TimerLayerChannelStore) GetMembersForUserWithCursor(userID string, afterChannel string, afterUser string, limit int, lastUpdateAt int) (model.ChannelMembers, error) { +func (s *TimerLayerChannelStore) GetMembersForUserWithCursor(userID string, teamID string, opts *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) { start := timemodule.Now() - result, err := s.ChannelStore.GetMembersForUserWithCursor(userID, afterChannel, afterUser, limit, lastUpdateAt) + result, err := s.ChannelStore.GetMembersForUserWithCursor(userID, teamID, opts) elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second) if s.Root.Metrics != nil {