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 ```
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
5ac3dbf058
Коммит
a6d8e45297
@@ -34,7 +34,7 @@ const (
|
||||
usersLoaderCtx ctxKey = 4
|
||||
)
|
||||
|
||||
const loaderBatchCapacity = web.PerPageMaximum + 100
|
||||
const loaderBatchCapacity = web.PerPageMaximum
|
||||
|
||||
//go:embed schema.graphqls
|
||||
var schemaRaw string
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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]!
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Ссылка в новой задаче
Block a user