MM-62158: group store no SELECT * (Part 1) (#30276)

* improved test coverage

* initial pass on removing SELECT * from group store
Этот коммит содержится в:
Jesse Hallam
2025-05-01 09:39:05 -03:00
коммит произвёл GitHub
родитель 80c58a9742
Коммит e1f47e22e7
2 изменённых файлов: 450 добавлений и 154 удалений

Просмотреть файл

@@ -49,10 +49,67 @@ type groupChannelJoin struct {
type SqlGroupStore struct { type SqlGroupStore struct {
*SqlStore *SqlStore
userGroupsSelectQuery sq.SelectBuilder
groupMembersSelectQuery sq.SelectBuilder
groupMemberUsersSelectQuery sq.SelectBuilder
groupTeamsSelectQuery sq.SelectBuilder
groupChannelsSelectQuery sq.SelectBuilder
} }
func newSqlGroupStore(sqlStore *SqlStore) store.GroupStore { func newSqlGroupStore(sqlStore *SqlStore) store.GroupStore {
return &SqlGroupStore{SqlStore: sqlStore} s := &SqlGroupStore{SqlStore: sqlStore}
s.userGroupsSelectQuery = s.getQueryBuilder().
Select(
"UserGroups.Id",
"UserGroups.Name",
"UserGroups.DisplayName",
"UserGroups.Description",
"UserGroups.Source",
"UserGroups.RemoteId",
"UserGroups.CreateAt",
"UserGroups.UpdateAt",
"UserGroups.DeleteAt",
"UserGroups.AllowReference",
).
From("UserGroups")
s.groupMembersSelectQuery = s.getQueryBuilder().
Select(
"GroupMembers.GroupId",
"GroupMembers.UserId",
"GroupMembers.CreateAt",
"GroupMembers.DeleteAt",
).From("GroupMembers")
s.groupMemberUsersSelectQuery = s.getQueryBuilder().
Select(getUsersColumns()...).
From("GroupMembers").
Join("Users ON Users.Id = GroupMembers.UserId")
s.groupTeamsSelectQuery = s.getQueryBuilder().
Select(
"GroupTeams.GroupId",
"GroupTeams.TeamId",
"GroupTeams.AutoAdd",
"GroupTeams.SchemeAdmin",
"GroupTeams.CreateAt",
"GroupTeams.UpdateAt",
"GroupTeams.DeleteAt",
).From("GroupTeams")
s.groupChannelsSelectQuery = s.getQueryBuilder().
Select(
"GroupChannels.GroupId",
"GroupChannels.ChannelId",
"GroupChannels.AutoAdd",
"GroupChannels.SchemeAdmin",
"GroupChannels.CreateAt",
"GroupChannels.UpdateAt",
"GroupChannels.DeleteAt",
).From("GroupChannels")
return s
} }
func (s *SqlGroupStore) Create(group *model.Group) (*model.Group, error) { func (s *SqlGroupStore) Create(group *model.Group) (*model.Group, error) {
@@ -132,14 +189,9 @@ func (s *SqlGroupStore) CreateWithUserIds(g *model.GroupWithUserIds) (_ *model.G
return nil, err return nil, err
} }
// Get the new Group along with the member count groupGroupQuery := s.userGroupsSelectQuery.
groupGroupQuery := ` Column("A.Count AS MemberCount").
SELECT InnerJoin(`(
UserGroups.*,
A.Count AS MemberCount
FROM
UserGroups
INNER JOIN (
SELECT SELECT
UserGroups.Id, UserGroups.Id,
COUNT(GroupMembers.UserId) AS Count COUNT(GroupMembers.UserId) AS Count
@@ -154,12 +206,12 @@ func (s *SqlGroupStore) CreateWithUserIds(g *model.GroupWithUserIds) (_ *model.G
UserGroups.DisplayName, UserGroups.DisplayName,
UserGroups.Id UserGroups.Id
LIMIT LIMIT
? OFFSET ? 1 OFFSET 0
) AS A ON UserGroups.Id = A.Id ) AS A ON UserGroups.Id = A.Id`, g.Id).
ORDER BY OrderBy("UserGroups.CreateAt DESC")
UserGroups.CreateAt DESC`
var newGroup group var newGroup group
if err = txn.Get(&newGroup, groupGroupQuery, g.Id, 1, 0); err != nil { if err = txn.GetBuilder(&newGroup, groupGroupQuery); err != nil {
return nil, err return nil, err
} }
if err = txn.Commit(); err != nil { if err = txn.Commit(); err != nil {
@@ -172,16 +224,13 @@ func (s *SqlGroupStore) checkUsersExist(userIDs []string) error {
if len(userIDs) == 0 { if len(userIDs) == 0 {
return nil return nil
} }
usersSelectQuery, usersSelectArgs, err := s.getQueryBuilder(). usersSelectQuery := s.getQueryBuilder().
Select("Id"). Select("Id").
From("Users"). From("Users").
Where(sq.Eq{"Id": userIDs, "DeleteAt": 0}). Where(sq.Eq{"Id": userIDs, "DeleteAt": 0})
ToSql()
if err != nil {
return err
}
var rows []string var rows []string
err = s.GetReplica().Select(&rows, usersSelectQuery, usersSelectArgs...) err := s.GetReplica().SelectBuilder(&rows, usersSelectQuery)
if err != nil { if err != nil {
return err return err
} }
@@ -215,9 +264,7 @@ func (s *SqlGroupStore) buildInsertGroupUsersQuery(groupId string, userIds []str
func (s *SqlGroupStore) Get(groupId string) (*model.Group, error) { func (s *SqlGroupStore) Get(groupId string) (*model.Group, error) {
var group model.Group var group model.Group
builder := s.getQueryBuilder(). builder := s.userGroupsSelectQuery.
Select("*").
From("UserGroups").
Where(sq.Eq{"Id": groupId}) Where(sq.Eq{"Id": groupId})
if err := s.GetReplica().GetBuilder(&group, builder); err != nil { if err := s.GetReplica().GetBuilder(&group, builder); err != nil {
@@ -232,16 +279,14 @@ func (s *SqlGroupStore) Get(groupId string) (*model.Group, error) {
func (s *SqlGroupStore) GetByName(name string, opts model.GroupSearchOpts) (*model.Group, error) { func (s *SqlGroupStore) GetByName(name string, opts model.GroupSearchOpts) (*model.Group, error) {
var group model.Group var group model.Group
query := s.getQueryBuilder().Select("*").From("UserGroups").Where(sq.Eq{"Name": name}) query := s.userGroupsSelectQuery.
Where(sq.Eq{"Name": name})
if opts.FilterAllowReference { if opts.FilterAllowReference {
query = query.Where("AllowReference = true") query = query.Where("AllowReference = true")
} }
queryString, args, err := query.ToSql() if err := s.GetReplica().GetBuilder(&group, query); err != nil {
if err != nil {
return nil, errors.Wrap(err, "get_by_name_tosql")
}
if err := s.GetReplica().Get(&group, queryString, args...); err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return nil, store.NewErrNotFound("Group", fmt.Sprintf("name=%s", name)) return nil, store.NewErrNotFound("Group", fmt.Sprintf("name=%s", name))
} }
@@ -253,12 +298,8 @@ func (s *SqlGroupStore) GetByName(name string, opts model.GroupSearchOpts) (*mod
func (s *SqlGroupStore) GetByIDs(groupIDs []string) ([]*model.Group, error) { func (s *SqlGroupStore) GetByIDs(groupIDs []string) ([]*model.Group, error) {
groups := []*model.Group{} groups := []*model.Group{}
query := s.getQueryBuilder().Select("*").From("UserGroups").Where(sq.Eq{"Id": groupIDs}) query := s.userGroupsSelectQuery.Where(sq.Eq{"Id": groupIDs})
queryString, args, err := query.ToSql() if err := s.GetReplica().SelectBuilder(&groups, query); err != nil {
if err != nil {
return nil, errors.Wrap(err, "get_by_ids_tosql")
}
if err := s.GetReplica().Select(&groups, queryString, args...); err != nil {
return nil, errors.Wrap(err, "failed to find Groups by ids") return nil, errors.Wrap(err, "failed to find Groups by ids")
} }
return groups, nil return groups, nil
@@ -266,9 +307,7 @@ func (s *SqlGroupStore) GetByIDs(groupIDs []string) ([]*model.Group, error) {
func (s *SqlGroupStore) GetByRemoteID(remoteID string, groupSource model.GroupSource) (*model.Group, error) { func (s *SqlGroupStore) GetByRemoteID(remoteID string, groupSource model.GroupSource) (*model.Group, error) {
var group model.Group var group model.Group
builder := s.getQueryBuilder(). builder := s.userGroupsSelectQuery.
Select("*").
From("UserGroups").
Where(sq.Eq{ Where(sq.Eq{
"RemoteId": remoteID, "RemoteId": remoteID,
"Source": groupSource, "Source": groupSource,
@@ -286,9 +325,7 @@ func (s *SqlGroupStore) GetByRemoteID(remoteID string, groupSource model.GroupSo
func (s *SqlGroupStore) GetAllBySource(groupSource model.GroupSource) ([]*model.Group, error) { func (s *SqlGroupStore) GetAllBySource(groupSource model.GroupSource) ([]*model.Group, error) {
groups := []*model.Group{} groups := []*model.Group{}
builder := s.getQueryBuilder(). builder := s.userGroupsSelectQuery.
Select("*").
From("UserGroups").
Where(sq.Eq{ Where(sq.Eq{
"DeleteAt": 0, "DeleteAt": 0,
"Source": groupSource, "Source": groupSource,
@@ -304,13 +341,11 @@ func (s *SqlGroupStore) GetAllBySource(groupSource model.GroupSource) ([]*model.
func (s *SqlGroupStore) GetByUser(userID string, opts model.GroupSearchOpts) ([]*model.Group, error) { func (s *SqlGroupStore) GetByUser(userID string, opts model.GroupSearchOpts) ([]*model.Group, error) {
groups := []*model.Group{} groups := []*model.Group{}
builder := s.getQueryBuilder(). builder := s.userGroupsSelectQuery.
Select("UserGroups.*"). Join("GroupMembers ON GroupMembers.GroupId = UserGroups.Id").
From("GroupMembers").
Join("UserGroups ON UserGroups.Id = GroupMembers.GroupId").
Where(sq.Eq{ Where(sq.Eq{
"GroupMembers.DeleteAt": 0, "GroupMembers.DeleteAt": 0,
"UserId": userID, "GroupMembers.UserId": userID,
}) })
if opts.FilterAllowReference { if opts.FilterAllowReference {
@@ -326,10 +361,7 @@ func (s *SqlGroupStore) GetByUser(userID string, opts model.GroupSearchOpts) ([]
func (s *SqlGroupStore) Update(group *model.Group) (*model.Group, error) { func (s *SqlGroupStore) Update(group *model.Group) (*model.Group, error) {
var retrievedGroup model.Group var retrievedGroup model.Group
builder := s.getQueryBuilder(). builder := s.userGroupsSelectQuery.Where(sq.Eq{"Id": group.Id})
Select("*").
From("UserGroups").
Where(sq.Eq{"Id": group.Id})
if err := s.GetReplica().GetBuilder(&retrievedGroup, builder); err != nil { if err := s.GetReplica().GetBuilder(&retrievedGroup, builder); err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
@@ -371,9 +403,7 @@ func (s *SqlGroupStore) Update(group *model.Group) (*model.Group, error) {
func (s *SqlGroupStore) Delete(groupID string) (*model.Group, error) { func (s *SqlGroupStore) Delete(groupID string) (*model.Group, error) {
var group model.Group var group model.Group
builder := s.getQueryBuilder(). builder := s.userGroupsSelectQuery.
Select("*").
From("UserGroups").
Where(sq.Eq{ Where(sq.Eq{
"Id": groupID, "Id": groupID,
"DeleteAt": 0, "DeleteAt": 0,
@@ -400,9 +430,7 @@ func (s *SqlGroupStore) Delete(groupID string) (*model.Group, error) {
func (s *SqlGroupStore) Restore(groupID string) (*model.Group, error) { func (s *SqlGroupStore) Restore(groupID string) (*model.Group, error) {
var group model.Group var group model.Group
builder := s.getQueryBuilder(). builder := s.userGroupsSelectQuery.
Select("*").
From("UserGroups").
Where(sq.And{ Where(sq.And{
sq.Eq{"Id": groupID}, sq.Eq{"Id": groupID},
sq.NotEq{"DeleteAt": 0}, sq.NotEq{"DeleteAt": 0},
@@ -427,12 +455,11 @@ func (s *SqlGroupStore) Restore(groupID string) (*model.Group, error) {
} }
func (s *SqlGroupStore) GetMember(groupID, userID string) (*model.GroupMember, error) { func (s *SqlGroupStore) GetMember(groupID, userID string) (*model.GroupMember, error) {
builder := s.getQueryBuilder(). builder := s.groupMembersSelectQuery.
Select("*").
From("GroupMembers").
Where(sq.Eq{"UserId": userID}). Where(sq.Eq{"UserId": userID}).
Where(sq.Eq{"GroupId": groupID}). Where(sq.Eq{"GroupId": groupID}).
Where(sq.Eq{"DeleteAt": 0}) Where(sq.Eq{"DeleteAt": 0})
var groupMember model.GroupMember var groupMember model.GroupMember
if err := s.GetReplica().GetBuilder(&groupMember, builder); err != nil { if err := s.GetReplica().GetBuilder(&groupMember, builder); err != nil {
return nil, errors.Wrap(err, "GetMember") return nil, errors.Wrap(err, "GetMember")
@@ -443,14 +470,11 @@ func (s *SqlGroupStore) GetMember(groupID, userID string) (*model.GroupMember, e
func (s *SqlGroupStore) GetMemberUsers(groupID string) ([]*model.User, error) { func (s *SqlGroupStore) GetMemberUsers(groupID string) ([]*model.User, error) {
groupMembers := []*model.User{} groupMembers := []*model.User{}
builder := s.getQueryBuilder(). builder := s.groupMemberUsersSelectQuery.
Select("Users.*").
From("GroupMembers").
Join("Users ON Users.Id = GroupMembers.UserId").
Where(sq.Eq{ Where(sq.Eq{
"GroupMembers.DeleteAt": 0, "GroupMembers.DeleteAt": 0,
"Users.DeleteAt": 0, "Users.DeleteAt": 0,
"GroupId": groupID, "GroupMembers.GroupId": groupID,
}) })
if err := s.GetReplica().SelectBuilder(&groupMembers, builder); err != nil { if err := s.GetReplica().SelectBuilder(&groupMembers, builder); err != nil {
@@ -467,23 +491,16 @@ func (s *SqlGroupStore) GetMemberUsersPage(groupID string, page int, perPage int
func (s *SqlGroupStore) GetMemberUsersSortedPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions, teammateNameDisplay string) ([]*model.User, error) { func (s *SqlGroupStore) GetMemberUsersSortedPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions, teammateNameDisplay string) ([]*model.User, error) {
groupMembers := []*model.User{} groupMembers := []*model.User{}
userQuery := s.getQueryBuilder(). userQuery := s.groupMemberUsersSelectQuery.
Select(`Users.*`).
From("GroupMembers").
Join("Users ON Users.Id = GroupMembers.UserId").
Where(sq.Eq{"GroupMembers.DeleteAt": 0}). Where(sq.Eq{"GroupMembers.DeleteAt": 0}).
Where(sq.Eq{"Users.DeleteAt": 0}). Where(sq.Eq{"Users.DeleteAt": 0}).
Where(sq.Eq{"GroupId": groupID}) Where(sq.Eq{"GroupMembers.GroupId": groupID})
userQuery = applyViewRestrictionsFilter(userQuery, viewRestrictions, true) userQuery = applyViewRestrictionsFilter(userQuery, viewRestrictions, true)
queryString, args, err := userQuery.ToSql()
if err != nil {
return nil, errors.Wrap(err, "")
}
orderQuery := s.getQueryBuilder(). orderQuery := s.getQueryBuilder().
Select("Users.*"). Select(getUsersColumns()...).
From("(" + queryString + ") AS Users") FromSelect(userQuery, "Users")
if teammateNameDisplay == model.ShowNicknameFullName { if teammateNameDisplay == model.ShowNicknameFullName {
orderQuery = orderQuery.OrderBy(` orderQuery = orderQuery.OrderBy(`
@@ -510,12 +527,7 @@ func (s *SqlGroupStore) GetMemberUsersSortedPage(groupID string, page int, perPa
Limit(uint64(perPage)). Limit(uint64(perPage)).
Offset(uint64(page * perPage)) Offset(uint64(page * perPage))
queryString, _, err = orderQuery.ToSql() if err := s.GetReplica().SelectBuilder(&groupMembers, orderQuery); err != nil {
if err != nil {
return nil, errors.Wrap(err, "")
}
if err := s.GetReplica().Select(&groupMembers, queryString, args...); err != nil {
return nil, errors.Wrapf(err, "failed to find member Users for Group with id=%s", groupID) return nil, errors.Wrapf(err, "failed to find member Users for Group with id=%s", groupID)
} }
@@ -525,9 +537,7 @@ func (s *SqlGroupStore) GetMemberUsersSortedPage(groupID string, page int, perPa
func (s *SqlGroupStore) GetNonMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, error) { func (s *SqlGroupStore) GetNonMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, error) {
groupMembers := []*model.User{} groupMembers := []*model.User{}
builder := s.getQueryBuilder(). builder := s.userGroupsSelectQuery.
Select("*").
From("UserGroups").
Where(sq.Eq{"Id": groupID}) Where(sq.Eq{"Id": groupID})
if err := s.GetReplica().GetBuilder(&model.Group{}, builder); err != nil { if err := s.GetReplica().GetBuilder(&model.Group{}, builder); err != nil {
@@ -535,7 +545,7 @@ func (s *SqlGroupStore) GetNonMemberUsersPage(groupID string, page int, perPage
} }
builder = s.getQueryBuilder(). builder = s.getQueryBuilder().
Select("Users.*"). Select(getUsersColumns()...).
From("Users"). From("Users").
LeftJoin("GroupMembers ON (GroupMembers.UserId = Users.Id AND GroupMembers.GroupId = ?)", groupID). LeftJoin("GroupMembers ON (GroupMembers.UserId = Users.Id AND GroupMembers.GroupId = ?)", groupID).
Where(sq.Eq{"Users.DeleteAt": 0}). Where(sq.Eq{"Users.DeleteAt": 0}).
@@ -568,14 +578,8 @@ func (s *SqlGroupStore) GetMemberCountWithRestrictions(groupID string, viewRestr
query = applyViewRestrictionsFilter(query, viewRestrictions, false) query = applyViewRestrictionsFilter(query, viewRestrictions, false)
queryString, args, err := query.ToSql()
if err != nil {
return int64(0), errors.Wrap(err, "")
}
var count int64 var count int64
err = s.GetReplica().Get(&count, queryString, args...) if err := s.GetReplica().GetBuilder(&count, query); err != nil {
if err != nil {
return int64(0), errors.Wrapf(err, "failed to count member Users for Group with id=%s", groupID) return int64(0), errors.Wrapf(err, "failed to count member Users for Group with id=%s", groupID)
} }
@@ -647,22 +651,22 @@ func (s *SqlGroupStore) GetMemberUsersNotInChannel(groupID string, channelID str
} }
func (s *SqlGroupStore) UpsertMember(groupID string, userID string) (*model.GroupMember, error) { func (s *SqlGroupStore) UpsertMember(groupID string, userID string) (*model.GroupMember, error) {
members, query, args, err := s.buildUpsertMembersQuery(groupID, []string{userID}) members, query, err := s.buildUpsertMembersQuery(groupID, []string{userID})
if err != nil { if err != nil {
return nil, err return nil, err
} }
if _, err = s.GetMaster().Exec(query, args...); err != nil { if _, err = s.GetMaster().ExecBuilder(query); err != nil {
return nil, errors.Wrap(err, "failed to save GroupMember") return nil, errors.Wrap(err, "failed to save GroupMember")
} }
return members[0], nil return members[0], nil
} }
func (s *SqlGroupStore) DeleteMember(groupID string, userID string) (*model.GroupMember, error) { func (s *SqlGroupStore) DeleteMember(groupID string, userID string) (*model.GroupMember, error) {
members, query, args, err := s.buildDeleteMembersQuery(groupID, []string{userID}) members, query, err := s.buildDeleteMembersQuery(groupID, []string{userID})
if err != nil { if err != nil {
return nil, err return nil, err
} }
if _, err = s.GetMaster().Exec(query, args...); err != nil { if _, err = s.GetMaster().ExecBuilder(query); err != nil {
return nil, errors.Wrapf(err, "failed to update GroupMember with groupId=%s and userId=%s", groupID, userID) return nil, errors.Wrapf(err, "failed to update GroupMember with groupId=%s and userId=%s", groupID, userID)
} }
@@ -742,11 +746,17 @@ func (s *SqlGroupStore) getGroupSyncable(groupID string, syncableID string, sync
switch syncableType { switch syncableType {
case model.GroupSyncableTypeTeam: case model.GroupSyncableTypeTeam:
var team groupTeam var team groupTeam
err = s.GetReplica().Get(&team, `SELECT * FROM GroupTeams WHERE GroupId=? AND TeamId=?`, groupID, syncableID) err = s.GetReplica().GetBuilder(&team, s.groupTeamsSelectQuery.Where(sq.Eq{
"GroupTeams.GroupId": groupID,
"GroupTeams.TeamId": syncableID,
}))
result = &team result = &team
case model.GroupSyncableTypeChannel: case model.GroupSyncableTypeChannel:
var ch groupChannel var ch groupChannel
err = s.GetReplica().Get(&ch, `SELECT * FROM GroupChannels WHERE GroupId=? AND ChannelId=?`, groupID, syncableID) err = s.GetReplica().GetBuilder(&ch, s.groupChannelsSelectQuery.Where(sq.Eq{
"GroupChannels.GroupId": groupID,
"GroupChannels.ChannelId": syncableID,
}))
result = &ch result = &ch
} }
@@ -790,19 +800,16 @@ func (s *SqlGroupStore) GetAllGroupSyncablesByGroupId(groupID string, syncableTy
switch syncableType { switch syncableType {
case model.GroupSyncableTypeTeam: case model.GroupSyncableTypeTeam:
sqlQuery := ` query := s.groupTeamsSelectQuery.
SELECT Columns("Teams.DisplayName AS TeamDisplayName", "Teams.Type AS TeamType").
GroupTeams.*, Join("Teams ON Teams.Id = GroupTeams.TeamId").
Teams.DisplayName AS TeamDisplayName, Where(sq.Eq{
Teams.Type AS TeamType "GroupTeams.GroupId": groupID,
FROM "GroupTeams.DeleteAt": 0,
GroupTeams })
JOIN Teams ON Teams.Id = GroupTeams.TeamId
WHERE
GroupId = ? AND GroupTeams.DeleteAt = 0`
results := []*groupTeamJoin{} results := []*groupTeamJoin{}
err := s.GetReplica().Select(&results, sqlQuery, groupID) err := s.GetReplica().SelectBuilder(&results, query)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to find GroupTeams with groupId=%s", groupID) return nil, errors.Wrapf(err, "failed to find GroupTeams with groupId=%s", groupID)
} }
@@ -822,23 +829,22 @@ func (s *SqlGroupStore) GetAllGroupSyncablesByGroupId(groupID string, syncableTy
groupSyncables = append(groupSyncables, groupSyncable) groupSyncables = append(groupSyncables, groupSyncable)
} }
case model.GroupSyncableTypeChannel: case model.GroupSyncableTypeChannel:
sqlQuery := ` query := s.groupChannelsSelectQuery.
SELECT Columns(
GroupChannels.*, "Channels.DisplayName AS ChannelDisplayName",
Channels.DisplayName AS ChannelDisplayName, "Teams.DisplayName AS TeamDisplayName",
Teams.DisplayName AS TeamDisplayName, "Channels.Type As ChannelType",
Channels.Type As ChannelType, "Teams.Type As TeamType",
Teams.Type As TeamType, "Teams.Id AS TeamId",
Teams.Id AS TeamId ).Join("Channels ON Channels.Id = GroupChannels.ChannelId").
FROM Join("Teams ON Teams.Id = Channels.TeamId").
GroupChannels Where(sq.Eq{
JOIN Channels ON Channels.Id = GroupChannels.ChannelId "GroupChannels.GroupId": groupID,
JOIN Teams ON Teams.Id = Channels.TeamId "GroupChannels.DeleteAt": 0,
WHERE })
GroupId = ? AND GroupChannels.DeleteAt = 0`
results := []*groupChannelJoin{} results := []*groupChannelJoin{}
err := s.GetReplica().Select(&results, sqlQuery, groupID) err := s.GetReplica().SelectBuilder(&results, query)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to find GroupChannels with groupId=%s", groupID) return nil, errors.Wrapf(err, "failed to find GroupChannels with groupId=%s", groupID)
} }
@@ -1902,22 +1908,22 @@ func (s *SqlGroupStore) countTableWithSelectAndWhere(selectStr, tableName string
} }
func (s *SqlGroupStore) UpsertMembers(groupID string, userIDs []string) ([]*model.GroupMember, error) { func (s *SqlGroupStore) UpsertMembers(groupID string, userIDs []string) ([]*model.GroupMember, error) {
members, query, args, err := s.buildUpsertMembersQuery(groupID, userIDs) members, query, err := s.buildUpsertMembersQuery(groupID, userIDs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if _, err = s.GetMaster().Exec(query, args...); err != nil { if _, err = s.GetMaster().ExecBuilder(query); err != nil {
return nil, errors.Wrap(err, "failed to save GroupMember") return nil, errors.Wrap(err, "failed to save GroupMember")
} }
return members, err return members, err
} }
func (s *SqlGroupStore) buildUpsertMembersQuery(groupID string, userIDs []string) (members []*model.GroupMember, query string, args []any, err error) { func (s *SqlGroupStore) buildUpsertMembersQuery(groupID string, userIDs []string) (members []*model.GroupMember, builder sq.InsertBuilder, err error) {
var retrievedGroup model.Group var retrievedGroup model.Group
// Check Group exists // Check Group exists
if err = s.GetReplica().Get(&retrievedGroup, "SELECT * FROM UserGroups WHERE Id = ?", groupID); err != nil { if err = s.GetReplica().GetBuilder(&retrievedGroup, s.userGroupsSelectQuery.Where(sq.Eq{"UserGroups.Id": groupID})); err != nil {
err = errors.Wrapf(err, "failed to get UserGroup with groupId=%s", groupID) err = errors.Wrapf(err, "failed to get UserGroup with groupId=%s", groupID)
return return
} }
@@ -1927,7 +1933,7 @@ func (s *SqlGroupStore) buildUpsertMembersQuery(groupID string, userIDs []string
return return
} }
builder := s.getQueryBuilder(). builder = s.getQueryBuilder().
Insert("GroupMembers"). Insert("GroupMembers").
Columns("GroupId", "UserId", "CreateAt", "DeleteAt") Columns("GroupId", "UserId", "CreateAt", "DeleteAt")
@@ -1950,38 +1956,30 @@ func (s *SqlGroupStore) buildUpsertMembersQuery(groupID string, userIDs []string
builder = builder.SuffixExpr(sq.Expr("ON CONFLICT (groupid, userid) DO UPDATE SET CreateAt = ?, DeleteAt = ?", createAt, 0)) builder = builder.SuffixExpr(sq.Expr("ON CONFLICT (groupid, userid) DO UPDATE SET CreateAt = ?, DeleteAt = ?", createAt, 0))
} }
query, args, err = builder.ToSql()
return return
} }
func (s *SqlGroupStore) DeleteMembers(groupID string, userIDs []string) ([]*model.GroupMember, error) { func (s *SqlGroupStore) DeleteMembers(groupID string, userIDs []string) ([]*model.GroupMember, error) {
members, query, args, err := s.buildDeleteMembersQuery(groupID, userIDs) members, query, err := s.buildDeleteMembersQuery(groupID, userIDs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if _, err = s.GetMaster().Exec(query, args...); err != nil { if _, err = s.GetMaster().ExecBuilder(query); err != nil {
return nil, errors.Wrap(err, "failed to delete GroupMembers") return nil, errors.Wrap(err, "failed to delete GroupMembers")
} }
return members, err return members, err
} }
func (s *SqlGroupStore) buildDeleteMembersQuery(groupID string, userIDs []string) (members []*model.GroupMember, query string, args []any, err error) { func (s *SqlGroupStore) buildDeleteMembersQuery(groupID string, userIDs []string) (members []*model.GroupMember, builder sq.UpdateBuilder, err error) {
membersSelectQuery, membersSelectArgs, err := s.getQueryBuilder(). membersSelectQuery := s.groupMembersSelectQuery.
Select("*").
From("GroupMembers").
Where(sq.And{ Where(sq.And{
sq.Eq{"GroupId": groupID}, sq.Eq{"GroupMembers.GroupId": groupID},
sq.Eq{"UserId": userIDs}, sq.Eq{"GroupMembers.UserId": userIDs},
sq.Eq{"DeleteAt": 0}, sq.Eq{"GroupMembers.DeleteAt": 0},
}). })
ToSql()
if err != nil {
return
}
err = s.GetReplica().Select(&members, membersSelectQuery, membersSelectArgs...) if err = s.GetReplica().SelectBuilder(&members, membersSelectQuery); err != nil {
if err != nil {
return return
} }
if len(members) != len(userIDs) { if len(members) != len(userIDs) {
@@ -2003,7 +2001,7 @@ func (s *SqlGroupStore) buildDeleteMembersQuery(groupID string, userIDs []string
member.DeleteAt = deleteAt member.DeleteAt = deleteAt
} }
builder := s.getQueryBuilder(). builder = s.getQueryBuilder().
Update("GroupMembers"). Update("GroupMembers").
Set("DeleteAt", deleteAt). Set("DeleteAt", deleteAt).
Where(sq.And{ Where(sq.And{
@@ -2011,6 +2009,5 @@ func (s *SqlGroupStore) buildDeleteMembersQuery(groupID string, userIDs []string
sq.Eq{"UserId": userIDs}, sq.Eq{"UserId": userIDs},
}) })
query, args, err = builder.ToSql()
return return
} }

Просмотреть файл

@@ -34,6 +34,7 @@ func TestGroupStore(t *testing.T, rctx request.CTX, ss store.Store) {
t.Run("Update", func(t *testing.T) { testGroupStoreUpdate(t, rctx, ss) }) t.Run("Update", func(t *testing.T) { testGroupStoreUpdate(t, rctx, ss) })
t.Run("Delete", func(t *testing.T) { testGroupStoreDelete(t, rctx, ss) }) t.Run("Delete", func(t *testing.T) { testGroupStoreDelete(t, rctx, ss) })
t.Run("Restore", func(t *testing.T) { testGroupStoreRestore(t, rctx, ss) }) t.Run("Restore", func(t *testing.T) { testGroupStoreRestore(t, rctx, ss) })
t.Run("ToModelChannelAssociations", func(t *testing.T) { testGroupStoreToModelChannelAssociations(t, rctx, ss) })
t.Run("GetMemberUsers", func(t *testing.T) { testGroupGetMemberUsers(t, rctx, ss) }) t.Run("GetMemberUsers", func(t *testing.T) { testGroupGetMemberUsers(t, rctx, ss) })
t.Run("GetMemberUsersPage", func(t *testing.T) { testGroupGetMemberUsersPage(t, rctx, ss) }) t.Run("GetMemberUsersPage", func(t *testing.T) { testGroupGetMemberUsersPage(t, rctx, ss) })
@@ -50,6 +51,7 @@ func TestGroupStore(t *testing.T, rctx request.CTX, ss store.Store) {
t.Run("CreateGroupSyncable", func(t *testing.T) { testCreateGroupSyncable(t, rctx, ss) }) t.Run("CreateGroupSyncable", func(t *testing.T) { testCreateGroupSyncable(t, rctx, ss) })
t.Run("GetGroupSyncable", func(t *testing.T) { testGetGroupSyncable(t, rctx, ss) }) t.Run("GetGroupSyncable", func(t *testing.T) { testGetGroupSyncable(t, rctx, ss) })
t.Run("GetGroupSyncableErrors", func(t *testing.T) { testGetGroupSyncableErrors(t, rctx, ss) })
t.Run("GetAllGroupSyncablesByGroupId", func(t *testing.T) { testGetAllGroupSyncablesByGroup(t, rctx, ss) }) t.Run("GetAllGroupSyncablesByGroupId", func(t *testing.T) { testGetAllGroupSyncablesByGroup(t, rctx, ss) })
t.Run("UpdateGroupSyncable", func(t *testing.T) { testUpdateGroupSyncable(t, rctx, ss) }) t.Run("UpdateGroupSyncable", func(t *testing.T) { testUpdateGroupSyncable(t, rctx, ss) })
t.Run("DeleteGroupSyncable", func(t *testing.T) { testDeleteGroupSyncable(t, rctx, ss) }) t.Run("DeleteGroupSyncable", func(t *testing.T) { testDeleteGroupSyncable(t, rctx, ss) })
@@ -74,6 +76,7 @@ func TestGroupStore(t *testing.T, rctx request.CTX, ss store.Store) {
t.Run("TeamMembersMinusGroupMembers", func(t *testing.T) { testTeamMembersMinusGroupMembers(t, rctx, ss) }) t.Run("TeamMembersMinusGroupMembers", func(t *testing.T) { testTeamMembersMinusGroupMembers(t, rctx, ss) })
t.Run("ChannelMembersMinusGroupMembers", func(t *testing.T) { testChannelMembersMinusGroupMembers(t, rctx, ss) }) t.Run("ChannelMembersMinusGroupMembers", func(t *testing.T) { testChannelMembersMinusGroupMembers(t, rctx, ss) })
t.Run("CountMembersMinusGroupMembers", func(t *testing.T) { testCountMembersMinusGroupMembers(t, rctx, ss) })
t.Run("GetMemberCount", func(t *testing.T) { groupTestGetMemberCount(t, rctx, ss) }) t.Run("GetMemberCount", func(t *testing.T) { groupTestGetMemberCount(t, rctx, ss) })
@@ -1618,6 +1621,44 @@ func testGetGroupSyncable(t *testing.T, rctx request.CTX, ss store.Store) {
require.Zero(t, gt1.DeleteAt) require.Zero(t, gt1.DeleteAt)
} }
func testGetGroupSyncableErrors(t *testing.T, rctx request.CTX, ss store.Store) {
// Create a group
g1 := &model.Group{
Name: model.NewPointer(model.NewId()),
DisplayName: model.NewId(),
Description: model.NewId(),
Source: model.GroupSourceLdap,
RemoteId: model.NewPointer(model.NewId()),
}
group, err := ss.Group().Create(g1)
require.NoError(t, err)
// Test with invalid syncable type
invalidSyncableType := model.GroupSyncableType("invalid")
_, err = ss.Group().GetGroupSyncable(group.Id, model.NewId(), invalidSyncableType)
require.Error(t, err)
var nfErr *store.ErrNotFound
require.True(t, errors.As(err, &nfErr), "expected ErrNotFound, got %v", err)
// Test with empty group ID
_, err = ss.Group().GetGroupSyncable("", model.NewId(), model.GroupSyncableTypeTeam)
require.True(t, errors.As(err, &nfErr), "expected ErrNotFound, got %v", err)
// Test with empty syncable ID
_, err = ss.Group().GetGroupSyncable(group.Id, "", model.GroupSyncableTypeTeam)
require.True(t, errors.As(err, &nfErr), "expected ErrNotFound, got %v", err)
// Test with completely non-existent IDs
randomGroupId := model.NewId()
randomTeamId := model.NewId()
_, err = ss.Group().GetGroupSyncable(randomGroupId, randomTeamId, model.GroupSyncableTypeTeam)
require.True(t, errors.As(err, &nfErr), "expected ErrNotFound, got %v", err)
// Test with valid group ID but non-existent syncable
_, err = ss.Group().GetGroupSyncable(group.Id, model.NewId(), model.GroupSyncableTypeTeam)
require.True(t, errors.As(err, &nfErr))
}
func testGetAllGroupSyncablesByGroup(t *testing.T, rctx request.CTX, ss store.Store) { func testGetAllGroupSyncablesByGroup(t *testing.T, rctx request.CTX, ss store.Store) {
t.Run("team", func(t *testing.T) { testGetAllGroupSyncablesByGroupTeam(t, rctx, ss) }) t.Run("team", func(t *testing.T) { testGetAllGroupSyncablesByGroupTeam(t, rctx, ss) })
t.Run("channel", func(t *testing.T) { testGetAllGroupSyncablesByGroupChannel(t, rctx, ss) }) t.Run("channel", func(t *testing.T) { testGetAllGroupSyncablesByGroupChannel(t, rctx, ss) })
@@ -4174,17 +4215,14 @@ func testGetGroups(t *testing.T, rctx request.CTX, ss store.Store) {
PerPage: 100, PerPage: 100,
Resultf: func(groups []*model.Group) bool { Resultf: func(groups []*model.Group) bool {
for _, group := range groups { for _, group := range groups {
fmt.Println(group.Id, group.ChannelMemberCount)
var channelMemberCount int var channelMemberCount int
if group.ChannelMemberCount != nil { if group.ChannelMemberCount != nil {
channelMemberCount = *group.ChannelMemberCount channelMemberCount = *group.ChannelMemberCount
} }
if group.Id == group1.Id && channelMemberCount != 2 { if group.Id == group1.Id && channelMemberCount != 2 {
fmt.Println("group1", group.Id, channelMemberCount)
return false return false
} }
if group.Id == group2.Id && channelMemberCount != 1 { if group.Id == group2.Id && channelMemberCount != 1 {
fmt.Println("group2", group.Id, channelMemberCount)
return false return false
} }
} }
@@ -4205,11 +4243,9 @@ func testGetGroups(t *testing.T, rctx request.CTX, ss store.Store) {
channelMemberCount = *group.ChannelMemberCount channelMemberCount = *group.ChannelMemberCount
} }
if group.Id == group1.Id && channelMemberCount != 1 { if group.Id == group1.Id && channelMemberCount != 1 {
fmt.Println("group1", group.Id, channelMemberCount)
return false return false
} }
if group.Id == group2.Id && channelMemberCount != 2 { if group.Id == group2.Id && channelMemberCount != 2 {
fmt.Println("group2", group.Id, channelMemberCount)
return false return false
} }
} }
@@ -5800,3 +5836,266 @@ func groupTestGroupCountBySource(t *testing.T, rctx request.CTX, ss store.Store)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, ldapSourceCountAfter-1, ldapSourceCountAfterDelete) require.Equal(t, ldapSourceCountAfter-1, ldapSourceCountAfterDelete)
} }
func testCountMembersMinusGroupMembers(t *testing.T, rctx request.CTX, ss store.Store) {
// Create test users
u1, err := ss.User().Save(rctx, &model.User{
Email: MakeEmail(),
Username: model.NewUsername(),
})
require.NoError(t, err)
u2, err := ss.User().Save(rctx, &model.User{
Email: MakeEmail(),
Username: model.NewUsername(),
})
require.NoError(t, err)
u3, err := ss.User().Save(rctx, &model.User{
Email: MakeEmail(),
Username: model.NewUsername(),
})
require.NoError(t, err)
// Create test team
team := &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.TeamOpen,
}
team, err = ss.Team().Save(team)
require.NoError(t, err)
// Create a second team to test team-specific counts
team2 := &model.Team{
DisplayName: "Name 2",
Description: "Some description 2",
CompanyName: "Some company name 2",
AllowOpenInvite: false,
InviteId: "inviteid1",
Name: "z-z-" + model.NewId() + "b",
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
Type: model.TeamOpen,
}
team2, err = ss.Team().Save(team2)
require.NoError(t, err)
// Create test channel in team 1
channel := &model.Channel{
TeamId: team.Id,
DisplayName: "Display Name",
Name: "z-z-" + model.NewId() + "a",
Type: model.ChannelTypeOpen,
}
channel, nErr := ss.Channel().Save(rctx, channel, -1)
require.NoError(t, nErr)
// Create test channel in team 2
channel2 := &model.Channel{
TeamId: team2.Id,
DisplayName: "Display Name 2",
Name: "z-z-" + model.NewId() + "b",
Type: model.ChannelTypeOpen,
}
channel2, nErr = ss.Channel().Save(rctx, channel2, -1)
require.NoError(t, nErr)
// Add users to teams and channels
// u1 and u2 in team 1
_, nErr = ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: team.Id, UserId: u1.Id}, -1)
require.NoError(t, nErr)
_, nErr = ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: team.Id, UserId: u2.Id}, -1)
require.NoError(t, nErr)
// u3 in team 2
_, nErr = ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: team2.Id, UserId: u3.Id}, -1)
require.NoError(t, nErr)
// u1 and u2 in channel 1
_, nErr = ss.Channel().SaveMember(rctx, &model.ChannelMember{ChannelId: channel.Id, UserId: u1.Id, NotifyProps: model.GetDefaultChannelNotifyProps()})
require.NoError(t, nErr)
_, nErr = ss.Channel().SaveMember(rctx, &model.ChannelMember{ChannelId: channel.Id, UserId: u2.Id, NotifyProps: model.GetDefaultChannelNotifyProps()})
require.NoError(t, nErr)
// u3 in channel 2
_, nErr = ss.Channel().SaveMember(rctx, &model.ChannelMember{ChannelId: channel2.Id, UserId: u3.Id, NotifyProps: model.GetDefaultChannelNotifyProps()})
require.NoError(t, nErr)
// Create groups
group1, err := ss.Group().Create(&model.Group{
Name: model.NewPointer(model.NewId()),
DisplayName: model.NewId(),
Description: model.NewId(),
Source: model.GroupSourceCustom,
RemoteId: model.NewPointer(model.NewId()),
})
require.NoError(t, err)
group2, err := ss.Group().Create(&model.Group{
Name: model.NewPointer(model.NewId()),
DisplayName: model.NewId(),
Description: model.NewId(),
Source: model.GroupSourceCustom,
RemoteId: model.NewPointer(model.NewId()),
})
require.NoError(t, err)
// Add u1 to group1, u3 to group2
_, err = ss.Group().UpsertMember(group1.Id, u1.Id)
require.NoError(t, err)
_, err = ss.Group().UpsertMember(group2.Id, u3.Id)
require.NoError(t, err)
// Test CountTeamMembersMinusGroupMembers with empty groupIDs
count, err := ss.Group().CountTeamMembersMinusGroupMembers(team.Id, []string{})
require.NoError(t, err)
require.Equal(t, int64(2), count) // Both u1 and u2 are counted when no groups are excluded
// Test CountTeamMembersMinusGroupMembers with group1 ID
count, err = ss.Group().CountTeamMembersMinusGroupMembers(team.Id, []string{group1.Id})
require.NoError(t, err)
require.Equal(t, int64(1), count) // Only u2 should be counted (not in the group)
// Test with non-existent team ID
count, err = ss.Group().CountTeamMembersMinusGroupMembers(model.NewId(), []string{group1.Id})
require.NoError(t, err)
require.Equal(t, int64(0), count) // No members in a non-existent team
// Test with multiple group IDs
count, err = ss.Group().CountTeamMembersMinusGroupMembers(team.Id, []string{group1.Id, group2.Id})
require.NoError(t, err)
require.Equal(t, int64(1), count) // Only u2 should be counted
// Test team 2
count, err = ss.Group().CountTeamMembersMinusGroupMembers(team2.Id, []string{group2.Id})
require.NoError(t, err)
require.Equal(t, int64(0), count) // No one should be counted (u3 is in group2)
// Test CountChannelMembersMinusGroupMembers with empty groupIDs
count, err = ss.Group().CountChannelMembersMinusGroupMembers(channel.Id, []string{})
require.NoError(t, err)
require.Equal(t, int64(2), count) // Both users are counted when no groups are excluded
// Test CountChannelMembersMinusGroupMembers with the group ID
count, err = ss.Group().CountChannelMembersMinusGroupMembers(channel.Id, []string{group1.Id})
require.NoError(t, err)
require.Equal(t, int64(1), count) // Only u2 should be counted (not in the group)
// Test with multiple group IDs
count, err = ss.Group().CountChannelMembersMinusGroupMembers(channel.Id, []string{group1.Id, group2.Id})
require.NoError(t, err)
require.Equal(t, int64(1), count) // u2 should be counted
// Test channel 2
count, err = ss.Group().CountChannelMembersMinusGroupMembers(channel2.Id, []string{group2.Id})
require.NoError(t, err)
require.Equal(t, int64(0), count) // No one should be counted (u3 is in group2)
// Test with non-existent channel ID
count, err = ss.Group().CountChannelMembersMinusGroupMembers(model.NewId(), []string{group1.Id})
require.NoError(t, err)
require.Equal(t, int64(0), count) // No members in a non-existent channel
// Test error cases - passing invalid parameters
// 1. Empty team ID
count, err = ss.Group().CountTeamMembersMinusGroupMembers("", []string{group1.Id})
require.NoError(t, err) // Should handle this gracefully
require.Equal(t, int64(0), count)
// 2. Empty channel ID
count, err = ss.Group().CountChannelMembersMinusGroupMembers("", []string{group1.Id})
require.NoError(t, err) // Should handle this gracefully
require.Equal(t, int64(0), count)
}
func testGroupStoreToModelChannelAssociations(t *testing.T, rctx request.CTX, ss store.Store) {
// Create test group
group, err := ss.Group().Create(&model.Group{
Name: model.NewPointer(model.NewId()),
DisplayName: model.NewId(),
Description: model.NewId(),
Source: model.GroupSourceCustom,
RemoteId: model.NewPointer(model.NewId()),
})
require.NoError(t, err)
require.NotNil(t, group)
// Create test team
team := &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.TeamOpen,
}
team, err = ss.Team().Save(team)
require.NoError(t, err)
require.NotNil(t, team)
// Create test channel 1
channel1 := &model.Channel{
TeamId: team.Id,
DisplayName: "Display Name 1",
Name: "z-z-" + model.NewId() + "a",
Type: model.ChannelTypeOpen,
}
channel1, nErr := ss.Channel().Save(rctx, channel1, -1)
require.NoError(t, nErr)
require.NotNil(t, channel1)
// Create test channel 2
channel2 := &model.Channel{
TeamId: team.Id,
DisplayName: "Display Name 2",
Name: "z-z-" + model.NewId() + "b",
Type: model.ChannelTypeOpen,
}
channel2, nErr = ss.Channel().Save(rctx, channel2, -1)
require.NoError(t, nErr)
require.NotNil(t, channel2)
// Create group channel syncables
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
GroupId: group.Id,
SyncableId: channel1.Id,
Type: model.GroupSyncableTypeChannel,
SchemeAdmin: true,
})
require.NoError(t, err)
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
GroupId: group.Id,
SyncableId: channel2.Id,
Type: model.GroupSyncableTypeChannel,
SchemeAdmin: false,
})
require.NoError(t, err)
// Test the GetGroupsAssociatedToChannelsByTeam function
// This exercises the groupsAssociatedToChannelWithSchemeAdmin.ToModel method
result, err := ss.Group().GetGroupsAssociatedToChannelsByTeam(team.Id, model.GroupSearchOpts{})
require.NoError(t, err)
require.NotNil(t, result)
// Verify channel 1 results
require.Contains(t, result, channel1.Id)
require.NotEmpty(t, result[channel1.Id])
require.Equal(t, group.Id, result[channel1.Id][0].Id)
require.NotNil(t, result[channel1.Id][0].SchemeAdmin)
require.True(t, *result[channel1.Id][0].SchemeAdmin)
// Verify channel 2 results (with different SchemeAdmin value)
require.Contains(t, result, channel2.Id)
require.NotEmpty(t, result[channel2.Id])
require.Equal(t, group.Id, result[channel2.Id][0].Id)
require.NotNil(t, result[channel2.Id][0].SchemeAdmin)
require.False(t, *result[channel2.Id][0].SchemeAdmin)
}