MM-15162: Changes for LDAP groups removals. (#10701)

* MM-15162: Changes for LDAP groups removals phase.

* MM-15162: Adds missing translation.

* MM-15162: Fixes tests.

* MM-15162: Removes some confusing branching.

* MM-15162: Make permission less restrictive.

* MM-15162: Moves counting to the DB tier.

* MM-15162: Moves CountGroupsByTeam into own store method.

* MM-15162: Adds count to tests.

* MM-15162: Fix for wrong cast type.

* MM-15162: Fix for possible null SchemeGuest column.

* MM-15162: Fixes bug whereby permissions error didn't return.

* MM-15162: Changes for LDAP groups removals phase.

* MM-15162: Adds missing translation.

* MM-15162: Fixes tests.

* MM-15162: Removes some confusing branching.

* MM-15162: Make permission less restrictive.

* MM-15162: Moves counting to the DB tier.

* MM-15162: Moves CountGroupsByTeam into own store method.

* MM-15162: Adds count to tests.

* MM-15162: Fix for wrong cast type.

* MM-15162: Fix for possible null SchemeGuest column.

* MM-15162: Fixes bug whereby permissions error didn't return.

* MM-15162: Adds missing translation blocking enterprise build.

* MM-15162: Update to group commands.
Этот коммит содержится в:
Martin Kraft
2019-05-10 11:47:21 -04:00
коммит произвёл GitHub
родитель 74c7c46a7d
Коммит 480fffd3cc
24 изменённых файлов: 1001 добавлений и 146 удалений

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

@@ -19,6 +19,9 @@ const (
)
func (api *API) InitGroup() {
// GET /api/v4/groups
api.BaseRoutes.Groups.Handle("", api.ApiSessionRequired(getGroups)).Methods("GET")
// GET /api/v4/groups/:group_id
api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}",
api.ApiSessionRequired(getGroup)).Methods("GET")
@@ -177,8 +180,9 @@ func linkGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
appErr := verifyLinkUnlinkPermission(c, syncableType, syncableID)
if appErr != nil {
c.Err = appErr
return
}
@@ -389,12 +393,13 @@ func unlinkGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
err := verifyLinkUnlinkPermission(c, syncableType, syncableID)
if err != nil {
c.Err = err
return
}
_, err := c.App.DeleteGroupSyncable(c.Params.GroupId, syncableID, syncableType)
_, err = c.App.DeleteGroupSyncable(c.Params.GroupId, syncableID, syncableType)
if err != nil {
c.Err = err
return
@@ -403,6 +408,33 @@ func unlinkGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
ReturnStatusOK(w)
}
func verifyLinkUnlinkPermission(c *Context, syncableType model.GroupSyncableType, syncableID string) *model.AppError {
switch syncableType {
case model.GroupSyncableTypeTeam:
if !c.App.SessionHasPermissionToTeam(c.App.Session, syncableID, model.PERMISSION_MANAGE_TEAM) {
return c.App.MakePermissionError(model.PERMISSION_MANAGE_TEAM)
}
case model.GroupSyncableTypeChannel:
channel, err := c.App.GetChannel(syncableID)
if err != nil {
return err
}
var permission *model.Permission
if channel.Type == model.CHANNEL_PRIVATE {
permission = model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS
} else {
permission = model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS
}
if !c.App.SessionHasPermissionToChannel(c.App.Session, syncableID, permission) {
return c.App.MakePermissionError(permission)
}
}
return nil
}
func getGroupMembers(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireGroupId()
if c.Err != nil {
@@ -482,18 +514,33 @@ func getGroupsByTeam(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
if !c.App.SessionHasPermissionToTeam(c.App.Session, c.Params.TeamId, model.PERMISSION_MANAGE_TEAM) {
c.SetPermissionError(model.PERMISSION_MANAGE_TEAM)
return
}
groups, err := c.App.GetGroupsByTeam(c.Params.TeamId, c.Params.Page, c.Params.PerPage)
opts := model.GroupSearchOpts{
Q: c.Params.Q,
IncludeMemberCount: c.Params.IncludeMemberCount,
}
if c.Params.Paginate == nil || *c.Params.Paginate {
opts.PageOpts = &model.PageOpts{Page: c.Params.Page, PerPage: c.Params.PerPage}
}
groups, totalCount, err := c.App.GetGroupsByTeam(c.Params.TeamId, opts)
if err != nil {
c.Err = err
return
}
b, marshalErr := json.Marshal(groups)
b, marshalErr := json.Marshal(struct {
Groups []*model.Group `json:"groups"`
Count int `json:"total_group_count"`
}{
Groups: groups,
Count: totalCount,
})
if marshalErr != nil {
c.Err = model.NewAppError("Api4.getGroupsByTeam", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
return
@@ -501,3 +548,38 @@ func getGroupsByTeam(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write(b)
}
func getGroups(c *Context, w http.ResponseWriter, r *http.Request) {
if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
c.Err = model.NewAppError("Api4.getGroups", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
return
}
opts := model.GroupSearchOpts{
Q: c.Params.Q,
IncludeMemberCount: c.Params.IncludeMemberCount,
}
teamID := c.Params.NotAssociatedToTeam
if len(teamID) == 26 {
if !c.App.SessionHasPermissionToTeam(c.App.Session, teamID, model.PERMISSION_VIEW_TEAM) {
c.SetPermissionError(model.PERMISSION_VIEW_TEAM)
return
}
opts.NotAssociatedToTeam = teamID
}
groups, err := c.App.GetGroups(c.Params.Page, c.Params.PerPage, opts)
if err != nil {
c.Err = err
return
}
b, marshalErr := json.Marshal(groups)
if marshalErr != nil {
c.Err = model.NewAppError("Api4.getGroups", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
return
}
w.Write(b)
}

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

@@ -9,6 +9,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/model"
)
@@ -150,7 +151,14 @@ func TestLinkGroupTeam(t *testing.T) {
th.App.SetLicense(model.NewTestLicense("ldap"))
groupTeam, response := th.SystemAdminClient.LinkGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
_, response = th.Client.LinkGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
assert.NotNil(t, response.Error)
th.UpdateUserToTeamAdmin(th.BasicUser, th.BasicTeam)
th.Client.Logout()
th.Client.Login(th.BasicUser.Email, th.BasicUser.Password)
groupTeam, response := th.Client.LinkGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
assert.Equal(t, http.StatusCreated, response.StatusCode)
assert.NotNil(t, groupTeam)
}
@@ -181,8 +189,17 @@ func TestLinkGroupChannel(t *testing.T) {
th.App.SetLicense(model.NewTestLicense("ldap"))
_, response = th.SystemAdminClient.LinkGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
groupTeam, response := th.Client.LinkGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
assert.Equal(t, http.StatusCreated, response.StatusCode)
assert.NotNil(t, groupTeam)
_, response = th.SystemAdminClient.UpdateChannelRoles(th.BasicChannel.Id, th.BasicUser.Id, "")
require.Nil(t, response.Error)
th.Client.Logout()
th.Client.Login(th.BasicUser.Email, th.BasicUser.Password)
_, response = th.Client.LinkGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
assert.NotNil(t, response.Error)
}
func TestUnlinkGroupTeam(t *testing.T) {
@@ -218,7 +235,14 @@ func TestUnlinkGroupTeam(t *testing.T) {
th.App.SetLicense(model.NewTestLicense("ldap"))
response = th.SystemAdminClient.UnlinkGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam)
response = th.Client.UnlinkGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam)
assert.NotNil(t, response.Error)
th.UpdateUserToTeamAdmin(th.BasicUser, th.BasicTeam)
th.Client.Logout()
th.Client.Login(th.BasicUser.Email, th.BasicUser.Password)
response = th.Client.UnlinkGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam)
CheckOKStatus(t, response)
}
@@ -255,8 +279,21 @@ func TestUnlinkGroupChannel(t *testing.T) {
th.App.SetLicense(model.NewTestLicense("ldap"))
response = th.SystemAdminClient.UnlinkGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel)
CheckOKStatus(t, response)
_, response = th.SystemAdminClient.UpdateChannelRoles(th.BasicChannel.Id, th.BasicUser.Id, "")
require.Nil(t, response.Error)
th.Client.Logout()
th.Client.Login(th.BasicUser.Email, th.BasicUser.Password)
response = th.Client.UnlinkGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel)
assert.NotNil(t, response.Error)
_, response = th.SystemAdminClient.UpdateChannelRoles(th.BasicChannel.Id, th.BasicUser.Id, "channel_admin channel_user")
require.Nil(t, response.Error)
th.Client.Logout()
th.Client.Login(th.BasicUser.Email, th.BasicUser.Password)
response = th.Client.UnlinkGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel)
assert.Nil(t, response.Error)
}
func TestGetGroupTeam(t *testing.T) {
@@ -661,24 +698,88 @@ func TestGetGroupsByTeam(t *testing.T) {
})
assert.Nil(t, err)
_, response := th.SystemAdminClient.GetGroupsByTeam("asdfasdf", 0, 60)
opts := model.GroupSearchOpts{
PageOpts: &model.PageOpts{
Page: 0,
PerPage: 60,
},
}
_, _, response := th.SystemAdminClient.GetGroupsByTeam("asdfasdf", opts)
CheckBadRequestStatus(t, response)
th.App.SetLicense(nil)
_, response = th.SystemAdminClient.GetGroupsByTeam(th.BasicTeam.Id, 0, 60)
_, _, response = th.SystemAdminClient.GetGroupsByTeam(th.BasicTeam.Id, opts)
CheckNotImplementedStatus(t, response)
th.App.SetLicense(model.NewTestLicense("ldap"))
_, response = th.Client.GetGroupsByTeam(th.BasicTeam.Id, 0, 60)
_, _, response = th.Client.GetGroupsByTeam(th.BasicTeam.Id, opts)
CheckForbiddenStatus(t, response)
groups, response := th.SystemAdminClient.GetGroupsByTeam(th.BasicTeam.Id, 0, 60)
groups, _, response := th.SystemAdminClient.GetGroupsByTeam(th.BasicTeam.Id, opts)
assert.Nil(t, response.Error)
assert.ElementsMatch(t, []*model.Group{group}, groups)
groups, response = th.SystemAdminClient.GetGroupsByTeam(model.NewId(), 0, 60)
groups, _, response = th.SystemAdminClient.GetGroupsByTeam(model.NewId(), opts)
assert.Nil(t, response.Error)
assert.Empty(t, groups)
}
func TestGetGroups(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
id := model.NewId()
group, err := th.App.CreateGroup(&model.Group{
DisplayName: "dn-foo_" + id,
Name: "name" + id,
Source: model.GroupSourceLdap,
Description: "description_" + id,
RemoteId: model.NewId(),
})
assert.Nil(t, err)
opts := model.GroupSearchOpts{
PageOpts: &model.PageOpts{
Page: 0,
PerPage: 60,
},
}
th.App.SetLicense(nil)
_, response := th.SystemAdminClient.GetGroups(opts)
CheckNotImplementedStatus(t, response)
th.App.SetLicense(model.NewTestLicense("ldap"))
groups, response := th.SystemAdminClient.GetGroups(opts)
assert.Nil(t, response.Error)
assert.ElementsMatch(t, []*model.Group{group, th.Group}, groups)
assert.Nil(t, groups[0].MemberCount)
opts.IncludeMemberCount = true
groups, _ = th.SystemAdminClient.GetGroups(opts)
assert.NotNil(t, groups[0].MemberCount)
opts.IncludeMemberCount = false
opts.Q = "-fOo"
groups, _ = th.SystemAdminClient.GetGroups(opts)
assert.Len(t, groups, 1)
opts.Q = ""
_, response = th.SystemAdminClient.UpdateTeamMemberRoles(th.BasicTeam.Id, th.BasicUser.Id, "")
require.Nil(t, response.Error)
opts.NotAssociatedToTeam = th.BasicTeam.Id
_, response = th.Client.GetGroups(opts)
CheckForbiddenStatus(t, response)
_, response = th.SystemAdminClient.UpdateTeamMemberRoles(th.BasicTeam.Id, th.BasicUser.Id, "team_user")
require.Nil(t, response.Error)
_, response = th.Client.GetGroups(opts)
assert.Nil(t, response.Error)
}

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

@@ -78,7 +78,7 @@ func getLdapGroups(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
opts := model.GroupSearchOpts{
opts := model.LdapGroupSearchOpts{
Q: c.Params.Q,
}
if c.Params.IsLinked != nil {

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

@@ -173,8 +173,24 @@ func (a *App) GetGroupsByChannel(channelId string, page, perPage int) ([]*model.
return result.Data.([]*model.Group), nil
}
func (a *App) GetGroupsByTeam(teamId string, page, perPage int) ([]*model.Group, *model.AppError) {
result := <-a.Srv.Store.Group().GetGroupsByTeam(teamId, page, perPage)
func (a *App) GetGroupsByTeam(teamId string, opts model.GroupSearchOpts) ([]*model.Group, int, *model.AppError) {
result := <-a.Srv.Store.Group().GetGroupsByTeam(teamId, opts)
if result.Err != nil {
return nil, 0, result.Err
}
groups := result.Data.([]*model.Group)
result = <-a.Srv.Store.Group().CountGroupsByTeam(teamId, opts)
if result.Err != nil {
return nil, 0, result.Err
}
count := result.Data.(int64)
return groups, int(count), nil
}
func (a *App) GetGroups(page, perPage int, opts model.GroupSearchOpts) ([]*model.Group, *model.AppError) {
result := <-a.Srv.Store.Group().GetGroups(page, perPage, opts)
if result.Err != nil {
return nil, result.Err
}

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

@@ -241,11 +241,21 @@ func TestGetGroupsByTeam(t *testing.T) {
require.Nil(t, err)
require.NotNil(t, gs)
groups, err := th.App.GetGroupsByTeam(th.BasicTeam.Id, 0, 60)
groups, _, err := th.App.GetGroupsByTeam(th.BasicTeam.Id, model.GroupSearchOpts{})
require.Nil(t, err)
require.ElementsMatch(t, []*model.Group{group}, groups)
groups, err = th.App.GetGroupsByTeam(model.NewId(), 0, 60)
groups, _, err = th.App.GetGroupsByTeam(model.NewId(), model.GroupSearchOpts{})
require.Nil(t, err)
require.Empty(t, groups)
}
func TestGetGroups(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
group := th.CreateGroup()
groups, err := th.App.GetGroups(0, 60, model.GroupSearchOpts{})
require.Nil(t, err)
require.ElementsMatch(t, []*model.Group{group}, groups)
}

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

@@ -61,7 +61,7 @@ func (a *App) GetLdapGroup(ldapGroupID string) (*model.Group, *model.AppError) {
// GetAllLdapGroupsPage retrieves all LDAP groups under the configured base DN using the default or configured group
// filter.
func (a *App) GetAllLdapGroupsPage(page int, perPage int, opts model.GroupSearchOpts) ([]*model.Group, int, *model.AppError) {
func (a *App) GetAllLdapGroupsPage(page int, perPage int, opts model.LdapGroupSearchOpts) ([]*model.Group, int, *model.AppError) {
var groups []*model.Group
var total int

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

@@ -222,7 +222,7 @@ func teamGroupEnableCmdF(command *cobra.Command, args []string) error {
return errors.New("Unable to find team '" + args[0] + "'")
}
groups, appErr := a.GetGroupsByTeam(team.Id, 0, 9999)
groups, _, appErr := a.GetGroupsByTeam(team.Id, model.GroupSearchOpts{})
if appErr != nil {
return appErr
}
@@ -292,7 +292,7 @@ func teamGroupListCmdF(command *cobra.Command, args []string) error {
return errors.New("Unable to find team '" + args[0] + "'")
}
groups, appErr := a.GetGroupsByTeam(team.Id, 0, 9999)
groups, _, appErr := a.GetGroupsByTeam(team.Id, model.GroupSearchOpts{})
if appErr != nil {
return appErr
}

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

@@ -19,6 +19,6 @@ type LdapInterface interface {
GetAllLdapUsers() ([]*model.User, *model.AppError)
MigrateIDAttribute(toAttribute string) error
GetGroup(groupUID string) (*model.Group, *model.AppError)
GetAllGroupsPage(page int, perPage int, opts model.GroupSearchOpts) ([]*model.Group, int, *model.AppError)
GetAllGroupsPage(page int, perPage int, opts model.LdapGroupSearchOpts) ([]*model.Group, int, *model.AppError)
FirstLoginSync(userID, userAuthService, userAuthData string) *model.AppError
}

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

@@ -5814,6 +5814,10 @@
"id": "store.sql_file_info.save.app_error",
"translation": "Unable to save the file info"
},
{
"id": "store.sql_group.app_error",
"translation": "failed to build query"
},
{
"id": "store.sql_group.group_syncable_already_deleted",
"translation": "group syncable was already deleted"

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

@@ -3310,7 +3310,7 @@ func (c *Client4) UnlinkLdapGroup(dn string) (*Group, *Response) {
return GroupFromJson(r.Body), BuildResponse(r)
}
// GetLdapGroupsByChannel retrieves the Mattermost Groups associated with a given channel
// GetGroupsByChannel retrieves the Mattermost Groups associated with a given channel
func (c *Client4) GetGroupsByChannel(channelId string, page, perPage int) ([]*Group, *Response) {
path := fmt.Sprintf("%s/groups?page=%v&per_page=%v", c.GetChannelRoute(channelId), page, perPage)
r, appErr := c.DoApiGet(path, "")
@@ -3322,9 +3322,39 @@ func (c *Client4) GetGroupsByChannel(channelId string, page, perPage int) ([]*Gr
return GroupsFromJson(r.Body), BuildResponse(r)
}
// GetLdapGroupsByTeam retrieves the Mattermost Groups associated with a given team
func (c *Client4) GetGroupsByTeam(teamId string, page, perPage int) ([]*Group, *Response) {
path := fmt.Sprintf("%s/groups?page=%v&per_page=%v", c.GetTeamRoute(teamId), page, perPage)
// GetGroupsByTeam retrieves the Mattermost Groups associated with a given team
func (c *Client4) GetGroupsByTeam(teamId string, opts GroupSearchOpts) ([]*Group, int, *Response) {
path := fmt.Sprintf("%s/groups?q=%v&include_member_count=%v", c.GetTeamRoute(teamId), opts.Q, opts.IncludeMemberCount)
if opts.PageOpts != nil {
path = fmt.Sprintf("%s&page=%v&per_page=%v", path, opts.PageOpts.Page, opts.PageOpts.PerPage)
}
r, appErr := c.DoApiGet(path, "")
if appErr != nil {
return nil, 0, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
responseData := struct {
Groups []*Group `json:"groups"`
Count int `json:"total_group_count"`
}{}
if err := json.NewDecoder(r.Body).Decode(&responseData); err != nil {
appErr := NewAppError("Api4.GetGroupsByTeam", "api.marshal_error", nil, err.Error(), http.StatusInternalServerError)
return nil, 0, BuildErrorResponse(r, appErr)
}
return responseData.Groups, responseData.Count, BuildResponse(r)
}
// GetGroups retrieves Mattermost Groups
func (c *Client4) GetGroups(opts GroupSearchOpts) ([]*Group, *Response) {
path := fmt.Sprintf(
"%s?include_member_count=%v&not_associated_to_team=%v&q=%v",
c.GetGroupsRoute(), opts.IncludeMemberCount, opts.NotAssociatedToTeam, opts.Q,
)
if opts.PageOpts != nil {
path = fmt.Sprintf("%s&page=%v&per_page=%v", path, opts.PageOpts.Page, opts.PageOpts.PerPage)
}
r, appErr := c.DoApiGet(path, "")
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)

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

@@ -40,6 +40,7 @@ type Group struct {
UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"`
HasSyncables bool `db:"-" json:"has_syncables"`
MemberCount *int `db:"-" json:"member_count,omitempty"`
}
type GroupPatch struct {
@@ -48,12 +49,24 @@ type GroupPatch struct {
Description *string `json:"description"`
}
type GroupSearchOpts struct {
type LdapGroupSearchOpts struct {
Q string
IsLinked *bool
IsConfigured *bool
}
type GroupSearchOpts struct {
Q string
NotAssociatedToTeam string
IncludeMemberCount bool
PageOpts *PageOpts
}
type PageOpts struct {
Page int
PerPage int
}
func (group *Group) Patch(patch *GroupPatch) {
if patch.Name != nil {
group.Name = *patch.Name

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

@@ -476,8 +476,20 @@ func (s *LayeredGroupStore) GetGroupsByChannel(channelId string, page, perPage i
})
}
func (s *LayeredGroupStore) GetGroupsByTeam(teamId string, page, perPage int) StoreChannel {
func (s *LayeredGroupStore) GetGroupsByTeam(teamId string, opts model.GroupSearchOpts) StoreChannel {
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
return supplier.GetGroupsByTeam(s.TmpContext, teamId, page, perPage)
return supplier.GetGroupsByTeam(s.TmpContext, teamId, opts)
})
}
func (s *LayeredGroupStore) CountGroupsByTeam(teamId string, opts model.GroupSearchOpts) StoreChannel {
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
return supplier.CountGroupsByTeam(s.TmpContext, teamId, opts)
})
}
func (s *LayeredGroupStore) GetGroups(page, perPage int, opts model.GroupSearchOpts) StoreChannel {
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
return supplier.GetGroups(s.TmpContext, page, perPage, opts)
})
}

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

@@ -75,5 +75,7 @@ type LayeredStoreSupplier interface {
ChannelMembersToRemove(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
GetGroupsByChannel(ctx context.Context, channelId string, page, perPage int, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
GetGroupsByTeam(ctx context.Context, teamId string, page, perPage int, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
GetGroupsByTeam(ctx context.Context, teamId string, opts model.GroupSearchOpts, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
CountGroupsByTeam(ctx context.Context, teamId string, opts model.GroupSearchOpts, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
GetGroups(ctx context.Context, page, perPage int, opts model.GroupSearchOpts, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
}

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

@@ -113,6 +113,14 @@ func (s *LocalCacheSupplier) GetGroupsByChannel(ctx context.Context, channelId s
return s.Next().GetGroupsByChannel(ctx, channelId, page, perPage, hints...)
}
func (s *LocalCacheSupplier) GetGroupsByTeam(ctx context.Context, teamId string, page, perPage int, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
return s.Next().GetGroupsByTeam(ctx, teamId, page, perPage, hints...)
func (s *LocalCacheSupplier) GetGroupsByTeam(ctx context.Context, teamId string, opts model.GroupSearchOpts, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
return s.Next().GetGroupsByTeam(ctx, teamId, opts, hints...)
}
func (s *LocalCacheSupplier) CountGroupsByTeam(ctx context.Context, teamId string, opts model.GroupSearchOpts, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
return s.Next().CountGroupsByTeam(ctx, teamId, opts, hints...)
}
func (s *LocalCacheSupplier) GetGroups(ctx context.Context, page, perPage int, opts model.GroupSearchOpts, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
return s.Next().GetGroups(ctx, page, perPage, opts, hints...)
}

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

@@ -114,7 +114,17 @@ func (s *RedisSupplier) GetGroupsByChannel(ctx context.Context, channelId string
return s.Next().GetGroupsByChannel(ctx, channelId, page, perPage, hints...)
}
func (s *RedisSupplier) GetGroupsByTeam(ctx context.Context, teamId string, page, perPage int, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
func (s *RedisSupplier) GetGroupsByTeam(ctx context.Context, teamId string, opts model.GroupSearchOpts, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
// TODO: Redis caching.
return s.Next().GetGroupsByTeam(ctx, teamId, page, perPage, hints...)
return s.Next().GetGroupsByTeam(ctx, teamId, opts, hints...)
}
func (s *RedisSupplier) CountGroupsByTeam(ctx context.Context, teamId string, opts model.GroupSearchOpts, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
// TODO: Redis caching.
return s.Next().CountGroupsByTeam(ctx, teamId, opts, hints...)
}
func (s *RedisSupplier) GetGroups(ctx context.Context, page, perPage int, opts model.GroupSearchOpts, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
// TODO: Redis caching.
return s.Next().GetGroups(ctx, page, perPage, opts, hints...)
}

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

@@ -9,10 +9,19 @@ import (
"fmt"
"net/http"
"github.com/Masterminds/squirrel"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
type selectType int
const (
selectGroups selectType = iota
selectCountGroups
)
type groupTeam struct {
model.GroupSyncable
TeamId string `db:"TeamId"`
@@ -771,7 +780,13 @@ func (s *SqlSupplier) TeamMembersToRemove(ctx context.Context, hints ...store.La
sql := `
SELECT
TeamMembers.*
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
@@ -854,7 +869,17 @@ func (s *SqlSupplier) ChannelMembersToRemove(ctx context.Context, hints ...store
sql := `
SELECT
ChannelMembers.*
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
@@ -894,32 +919,81 @@ func (s *SqlSupplier) ChannelMembersToRemove(ctx context.Context, hints ...store
return result
}
func (s *SqlSupplier) GetGroupsByTeam(ctx context.Context, teamId string, page, perPage int, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
func (s *SqlSupplier) groupsByTeamBaseQuery(t selectType, teamID string, opts model.GroupSearchOpts) squirrel.SelectBuilder {
selectStrs := map[selectType]string{
selectGroups: "ug.*",
selectCountGroups: "COUNT(*)",
}
query := s.getQueryBuilder().
Select(selectStrs[t]).
From("GroupTeams gt").
LeftJoin("UserGroups ug ON gt.GroupId = ug.Id").
Where("ug.DeleteAt = 0 AND gt.TeamId = ? AND gt.DeleteAt = 0", teamID)
if opts.IncludeMemberCount && t == selectGroups {
query = s.getQueryBuilder().
Select("ug.*, coalesce(Members.MemberCount, 0) AS MemberCount").
From("UserGroups ug").
LeftJoin("(SELECT GroupMembers.GroupId, COUNT(*) AS MemberCount FROM GroupMembers WHERE GroupMembers.DeleteAt = 0 GROUP BY GroupId) AS Members ON Members.GroupId = ug.Id").
LeftJoin("GroupTeams ON GroupTeams.GroupId = ug.Id").
Where("GroupTeams.DeleteAt = 0 AND GroupTeams.TeamId = ?", teamID).
OrderBy("ug.DisplayName")
}
if len(opts.Q) > 0 {
pattern := fmt.Sprintf("%%%s%%", opts.Q)
operatorKeyword := "ILIKE"
if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
operatorKeyword = "LIKE"
}
query = query.Where(fmt.Sprintf("(ug.Name %[1]s ? OR ug.DisplayName %[1]s ?)", operatorKeyword), pattern, pattern)
}
return query
}
func (s *SqlSupplier) CountGroupsByTeam(ctx context.Context, teamId string, opts model.GroupSearchOpts, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()
var groups []*model.Group
offset := page * perPage
_, err := s.GetReplica().Select(&groups, `
SELECT
ug.*
FROM
GroupTeams gt
LEFT JOIN
UserGroups ug
ON
gt.GroupId = ug.Id
WHERE
gt.DeleteAt = 0
AND
ug.DeleteAt = 0
AND
gt.TeamId = :TeamId
ORDER BY
ug.DisplayName
LIMIT :Limit
OFFSET :Offset`,
map[string]interface{}{"TeamId": teamId, "Limit": perPage, "Offset": offset})
countQuery := s.groupsByTeamBaseQuery(selectCountGroups, teamId, opts)
countQueryString, args, err := countQuery.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlGroupStore.CountGroupsByTeam", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
return result
}
count, err := s.GetReplica().SelectInt(countQueryString, args...)
if err != nil {
result.Err = model.NewAppError("SqlGroupStore.CountGroupsByTeam", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
return result
}
result.Data = count
return result
}
func (s *SqlSupplier) GetGroupsByTeam(ctx context.Context, teamId string, opts model.GroupSearchOpts, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()
query := s.groupsByTeamBaseQuery(selectGroups, teamId, opts)
if opts.PageOpts != nil {
offset := uint64(opts.PageOpts.Page * opts.PageOpts.PerPage)
query = query.OrderBy("ug.DisplayName").Limit(uint64(opts.PageOpts.PerPage)).Offset(offset)
}
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlGroupStore.GetGroupsByTeam", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
return result
}
var groups []*model.Group
_, err = s.GetReplica().Select(&groups, queryString, args...)
if err != nil {
result.Err = model.NewAppError("SqlGroupStore.GetGroupsByTeam", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
return result
@@ -929,3 +1003,59 @@ func (s *SqlSupplier) GetGroupsByTeam(ctx context.Context, teamId string, page,
return result
}
func (s *SqlSupplier) GetGroups(ctx context.Context, page, perPage int, opts model.GroupSearchOpts, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()
var groups []*model.Group
groupsQuery := s.getQueryBuilder().Select("g.*").From("UserGroups g").Limit(uint64(perPage)).Offset(uint64(page * perPage)).OrderBy("g.DisplayName")
if opts.IncludeMemberCount {
groupsQuery = s.getQueryBuilder().
Select("g.*, coalesce(Members.MemberCount, 0) AS MemberCount").
From("UserGroups g").
LeftJoin("(SELECT GroupMembers.GroupId, COUNT(*) AS MemberCount FROM GroupMembers WHERE GroupMembers.DeleteAt = 0 GROUP BY GroupId) AS Members ON Members.GroupId = g.Id").
Limit(uint64(perPage)).
Offset(uint64(page * perPage)).
OrderBy("g.DisplayName")
}
if len(opts.Q) > 0 {
pattern := fmt.Sprintf("%%%s%%", opts.Q)
operatorKeyword := "ILIKE"
if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
operatorKeyword = "LIKE"
}
groupsQuery = groupsQuery.Where(fmt.Sprintf("(g.Name %[1]s ? OR g.DisplayName %[1]s ?)", operatorKeyword), pattern, pattern)
}
if len(opts.NotAssociatedToTeam) == 26 {
groupsQuery = groupsQuery.Where(`
g.Id NOT IN (
SELECT
Id
FROM
UserGroups
JOIN GroupTeams ON GroupTeams.GroupId = UserGroups.Id
WHERE
GroupTeams.DeleteAt = 0
AND UserGroups.DeleteAt = 0
AND GroupTeams.TeamId = ?
)
`, opts.NotAssociatedToTeam)
}
queryString, args, err := groupsQuery.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlGroupStore.GetGroups", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
return result
}
if _, err = s.GetReplica().Select(&groups, queryString, args...); err != nil {
result.Err = model.NewAppError("SqlGroupStore.GetGroups", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
return result
}
result.Data = groups
return result
}

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

@@ -596,7 +596,9 @@ type GroupStore interface {
ChannelMembersToRemove() StoreChannel
GetGroupsByChannel(channelId string, page, perPage int) StoreChannel
GetGroupsByTeam(teamId string, page, perPage int) StoreChannel
GetGroupsByTeam(teamId string, opts model.GroupSearchOpts) StoreChannel
CountGroupsByTeam(teamId string, opts model.GroupSearchOpts) StoreChannel
GetGroups(page, perPage int, opts model.GroupSearchOpts) StoreChannel
}
type LinkMetadataStore interface {

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

@@ -4,7 +4,6 @@
package storetest
import (
"fmt"
"sort"
"strconv"
"strings"
@@ -2722,7 +2721,6 @@ func testChannelStoreGetChannelsByScheme(t *testing.T, ss store.Store) {
}
result := <-ss.Scheme().Save(s1)
fmt.Println(result.Err)
s1 = result.Data.(*model.Scheme)
s1 = (<-ss.Scheme().Save(s1)).Data.(*model.Scheme)
s2 = (<-ss.Scheme().Save(s2)).Data.(*model.Scheme)

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

@@ -39,6 +39,8 @@ func TestGroupStore(t *testing.T, ss store.Store) {
t.Run("GetGroupsByChannel", func(t *testing.T) { testGetGroupsByChannel(t, ss) })
t.Run("GetGroupsByTeam", func(t *testing.T) { testGetGroupsByTeam(t, ss) })
t.Run("GetGroups", func(t *testing.T) { testGetGroups(t, ss) })
}
func testGroupStoreCreate(t *testing.T, ss store.Store) {
@@ -1762,23 +1764,44 @@ func testGetGroupsByTeam(t *testing.T, ss store.Store) {
})
require.Nil(t, res.Err)
// add members
u1 := &model.User{
Email: MakeEmail(),
Username: model.NewId(),
}
res = <-ss.User().Save(u1)
require.Nil(t, res.Err)
user1 := res.Data.(*model.User)
<-ss.Group().CreateOrRestoreMember(group1.Id, user1.Id)
group1WithMemberCount := model.Group(*group1)
group1WithMemberCount.MemberCount = model.NewInt(1)
group2WithMemberCount := model.Group(*group2)
group2WithMemberCount.MemberCount = model.NewInt(0)
testCases := []struct {
Name string
TeamId string
Page int
PerPage int
Result []*model.Group
Name string
TeamId string
Page int
PerPage int
Opts model.GroupSearchOpts
Result []*model.Group
TotalCount *int64
}{
{
Name: "Get the two Groups for Team1",
TeamId: team1.Id,
Page: 0,
PerPage: 60,
Result: []*model.Group{group1, group2},
Name: "Get the two Groups for Team1",
TeamId: team1.Id,
Opts: model.GroupSearchOpts{},
Page: 0,
PerPage: 60,
Result: []*model.Group{group1, group2},
TotalCount: model.NewInt64(2),
},
{
Name: "Get first Group for Team1 with page 0 with 1 element",
TeamId: team1.Id,
Opts: model.GroupSearchOpts{},
Page: 0,
PerPage: 1,
Result: []*model.Group{group1},
@@ -1786,31 +1809,309 @@ func testGetGroupsByTeam(t *testing.T, ss store.Store) {
{
Name: "Get second Group for Team1 with page 1 with 1 element",
TeamId: team1.Id,
Opts: model.GroupSearchOpts{},
Page: 1,
PerPage: 1,
Result: []*model.Group{group2},
},
{
Name: "Get third Group for Team2",
TeamId: team2.Id,
Page: 0,
PerPage: 60,
Result: []*model.Group{group3},
Name: "Get third Group for Team2",
TeamId: team2.Id,
Opts: model.GroupSearchOpts{},
Page: 0,
PerPage: 60,
Result: []*model.Group{group3},
TotalCount: model.NewInt64(1),
},
{
Name: "Get empty Groups for a fake id",
TeamId: model.NewId(),
Name: "Get empty Groups for a fake id",
TeamId: model.NewId(),
Opts: model.GroupSearchOpts{},
Page: 0,
PerPage: 60,
Result: []*model.Group{},
TotalCount: model.NewInt64(0),
},
{
Name: "Get group matching name",
TeamId: team1.Id,
Opts: model.GroupSearchOpts{Q: string([]rune(group1.Name)[2:10])}, // very low change of a name collision
Page: 0,
PerPage: 100,
Result: []*model.Group{group1},
TotalCount: model.NewInt64(1),
},
{
Name: "Get group matching display name",
TeamId: team1.Id,
Opts: model.GroupSearchOpts{Q: "rouP-1"},
Page: 0,
PerPage: 100,
Result: []*model.Group{group1},
TotalCount: model.NewInt64(1),
},
{
Name: "Get group matching multiple display names",
TeamId: team1.Id,
Opts: model.GroupSearchOpts{Q: "roUp-"},
Page: 0,
PerPage: 100,
Result: []*model.Group{group1, group2},
TotalCount: model.NewInt64(2),
},
{
Name: "Include member counts",
TeamId: team1.Id,
Opts: model.GroupSearchOpts{IncludeMemberCount: true},
Page: 0,
PerPage: 60,
Result: []*model.Group{},
PerPage: 2,
Result: []*model.Group{&group1WithMemberCount, &group2WithMemberCount},
},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
res := <-ss.Group().GetGroupsByTeam(tc.TeamId, tc.Page, tc.PerPage)
if tc.Opts.PageOpts == nil {
tc.Opts.PageOpts = &model.PageOpts{}
}
tc.Opts.PageOpts.Page = tc.Page
tc.Opts.PageOpts.PerPage = tc.PerPage
res := <-ss.Group().GetGroupsByTeam(tc.TeamId, tc.Opts)
require.Nil(t, res.Err)
require.ElementsMatch(t, tc.Result, res.Data.([]*model.Group))
groups := res.Data.([]*model.Group)
require.ElementsMatch(t, tc.Result, groups)
if tc.TotalCount != nil {
res = <-ss.Group().CountGroupsByTeam(tc.TeamId, tc.Opts)
count := res.Data.(int64)
require.Equal(t, *tc.TotalCount, count)
}
})
}
}
func testGetGroups(t *testing.T, ss store.Store) {
// Create Team1
team1 := &model.Team{
DisplayName: "Team1",
Description: model.NewId(),
CompanyName: model.NewId(),
AllowOpenInvite: false,
InviteId: model.NewId(),
Name: model.NewId(),
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
Type: model.TEAM_OPEN,
}
team1, err := ss.Team().Save(team1)
require.Nil(t, err)
// Create Groups 1 and 2
res := <-ss.Group().Create(&model.Group{
Name: model.NewId(),
DisplayName: "group-1",
RemoteId: model.NewId(),
Source: model.GroupSourceLdap,
})
require.Nil(t, res.Err)
group1 := res.Data.(*model.Group)
res = <-ss.Group().Create(&model.Group{
Name: model.NewId(),
DisplayName: "group-2",
RemoteId: model.NewId(),
Source: model.GroupSourceLdap,
})
require.Nil(t, res.Err)
group2 := res.Data.(*model.Group)
// And associate them with Team1
for _, g := range []*model.Group{group1, group2} {
res = <-ss.Group().CreateGroupSyncable(&model.GroupSyncable{
AutoAdd: true,
SyncableId: team1.Id,
Type: model.GroupSyncableTypeTeam,
GroupId: g.Id,
})
require.Nil(t, res.Err)
}
// Create Team2
team2 := &model.Team{
DisplayName: "Team2",
Description: model.NewId(),
CompanyName: model.NewId(),
AllowOpenInvite: false,
InviteId: model.NewId(),
Name: model.NewId(),
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
Type: model.TEAM_INVITE,
}
team2, err = ss.Team().Save(team2)
require.Nil(t, err)
// Create Group3
res = <-ss.Group().Create(&model.Group{
Name: model.NewId(),
DisplayName: "group-3",
RemoteId: model.NewId(),
Source: model.GroupSourceLdap,
})
require.Nil(t, res.Err)
group3 := res.Data.(*model.Group)
// And associate it to Team2
res = <-ss.Group().CreateGroupSyncable(&model.GroupSyncable{
AutoAdd: true,
SyncableId: team2.Id,
Type: model.GroupSyncableTypeTeam,
GroupId: group3.Id,
})
require.Nil(t, res.Err)
// add members
u1 := &model.User{
Email: MakeEmail(),
Username: model.NewId(),
}
res = <-ss.User().Save(u1)
require.Nil(t, res.Err)
user1 := res.Data.(*model.User)
<-ss.Group().CreateOrRestoreMember(group1.Id, user1.Id)
group1WithMemberCount := model.Group(*group1)
group1WithMemberCount.MemberCount = model.NewInt(1)
group2WithMemberCount := model.Group(*group2)
group2WithMemberCount.MemberCount = model.NewInt(0)
group2NameSubstring := string([]rune(group2.Name)[2:5])
testCases := []struct {
Name string
Page int
PerPage int
Opts model.GroupSearchOpts
Resultf func([]*model.Group) bool
}{
{
Name: "Get all the Groups",
Opts: model.GroupSearchOpts{},
Page: 0,
PerPage: 3,
Resultf: func(groups []*model.Group) bool { return len(groups) == 3 },
},
{
Name: "Get first Group with page 0 with 1 element",
Opts: model.GroupSearchOpts{},
Page: 0,
PerPage: 1,
Resultf: func(groups []*model.Group) bool { return len(groups) == 1 },
},
{
Name: "Get single result from page 1",
Opts: model.GroupSearchOpts{},
Page: 1,
PerPage: 1,
Resultf: func(groups []*model.Group) bool { return len(groups) == 1 },
},
{
Name: "Get multiple results from page 1",
Opts: model.GroupSearchOpts{},
Page: 1,
PerPage: 2,
Resultf: func(groups []*model.Group) bool { return len(groups) == 2 },
},
{
Name: "Get group matching name",
Opts: model.GroupSearchOpts{Q: group2NameSubstring},
Page: 0,
PerPage: 100,
Resultf: func(groups []*model.Group) bool {
for _, g := range groups {
if !strings.Contains(g.Name, group2NameSubstring) {
return false
}
}
return true
},
},
{
Name: "Get group matching display name",
Opts: model.GroupSearchOpts{Q: "rouP-3"},
Page: 0,
PerPage: 100,
Resultf: func(groups []*model.Group) bool {
for _, g := range groups {
if !strings.Contains(strings.ToLower(g.DisplayName), "roup-3") {
return false
}
}
return true
},
},
{
Name: "Get group matching multiple display names",
Opts: model.GroupSearchOpts{Q: "groUp"},
Page: 0,
PerPage: 100,
Resultf: func(groups []*model.Group) bool {
for _, g := range groups {
if !strings.Contains(strings.ToLower(g.DisplayName), "group") {
return false
}
}
return true
},
},
{
Name: "Include member counts",
Opts: model.GroupSearchOpts{IncludeMemberCount: true},
Page: 0,
PerPage: 2,
Resultf: func(groups []*model.Group) bool {
for _, g := range groups {
if g.MemberCount == nil {
return false
}
}
return true
},
},
{
Name: "Not associated to team",
Opts: model.GroupSearchOpts{NotAssociatedToTeam: team2.Id},
Page: 0,
PerPage: 100,
Resultf: func(groups []*model.Group) bool {
for _, g := range groups {
if g.Id == group3.Id {
return false
}
}
return true
},
},
{
Name: "Not associated to other team",
Opts: model.GroupSearchOpts{NotAssociatedToTeam: team1.Id},
Page: 0,
PerPage: 100,
Resultf: func(groups []*model.Group) bool {
for _, g := range groups {
if g.Id == group1.Id || g.Id == group2.Id {
return false
}
}
return true
},
},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
res := <-ss.Group().GetGroups(tc.Page, tc.PerPage, tc.Opts)
require.Nil(t, res.Err)
groups := res.Data.([]*model.Group)
require.True(t, tc.Resultf(groups))
})
}
}

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

@@ -45,6 +45,22 @@ func (_m *GroupStore) ChannelMembersToRemove() store.StoreChannel {
return r0
}
// CountGroupsByTeam provides a mock function with given fields: teamId, opts
func (_m *GroupStore) CountGroupsByTeam(teamId string, opts model.GroupSearchOpts) store.StoreChannel {
ret := _m.Called(teamId, opts)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, model.GroupSearchOpts) store.StoreChannel); ok {
r0 = rf(teamId, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// Create provides a mock function with given fields: group
func (_m *GroupStore) Create(group *model.Group) store.StoreChannel {
ret := _m.Called(group)
@@ -221,6 +237,22 @@ func (_m *GroupStore) GetGroupSyncable(groupID string, syncableID string, syncab
return r0
}
// GetGroups provides a mock function with given fields: page, perPage, opts
func (_m *GroupStore) GetGroups(page int, perPage int, opts model.GroupSearchOpts) store.StoreChannel {
ret := _m.Called(page, perPage, opts)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(int, int, model.GroupSearchOpts) store.StoreChannel); ok {
r0 = rf(page, perPage, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// GetGroupsByChannel provides a mock function with given fields: channelId, page, perPage
func (_m *GroupStore) GetGroupsByChannel(channelId string, page int, perPage int) store.StoreChannel {
ret := _m.Called(channelId, page, perPage)
@@ -237,13 +269,13 @@ func (_m *GroupStore) GetGroupsByChannel(channelId string, page int, perPage int
return r0
}
// GetGroupsByTeam provides a mock function with given fields: teamId, page, perPage
func (_m *GroupStore) GetGroupsByTeam(teamId string, page int, perPage int) store.StoreChannel {
ret := _m.Called(teamId, page, perPage)
// GetGroupsByTeam provides a mock function with given fields: teamId, opts
func (_m *GroupStore) GetGroupsByTeam(teamId string, opts model.GroupSearchOpts) store.StoreChannel {
ret := _m.Called(teamId, opts)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, int, int) store.StoreChannel); ok {
r0 = rf(teamId, page, perPage)
if rf, ok := ret.Get(0).(func(string, model.GroupSearchOpts) store.StoreChannel); ok {
r0 = rf(teamId, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)

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

@@ -193,6 +193,29 @@ func (_m *LayeredStoreDatabaseLayer) Compliance() store.ComplianceStore {
return r0
}
// CountGroupsByTeam provides a mock function with given fields: ctx, teamId, opts, hints
func (_m *LayeredStoreDatabaseLayer) CountGroupsByTeam(ctx context.Context, teamId string, opts model.GroupSearchOpts, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, teamId, opts)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, string, model.GroupSearchOpts, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, teamId, opts, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// DropAllTables provides a mock function with given fields:
func (_m *LayeredStoreDatabaseLayer) DropAllTables() {
_m.Called()
@@ -230,6 +253,29 @@ func (_m *LayeredStoreDatabaseLayer) FileInfo() store.FileInfoStore {
return r0
}
// GetGroups provides a mock function with given fields: ctx, page, perPage, opts, hints
func (_m *LayeredStoreDatabaseLayer) GetGroups(ctx context.Context, page int, perPage int, opts model.GroupSearchOpts, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, page, perPage, opts)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, int, int, model.GroupSearchOpts, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, page, perPage, opts, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// GetGroupsByChannel provides a mock function with given fields: ctx, channelId, page, perPage, hints
func (_m *LayeredStoreDatabaseLayer) GetGroupsByChannel(ctx context.Context, channelId string, page int, perPage int, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
@@ -253,20 +299,20 @@ func (_m *LayeredStoreDatabaseLayer) GetGroupsByChannel(ctx context.Context, cha
return r0
}
// GetGroupsByTeam provides a mock function with given fields: ctx, teamId, page, perPage, hints
func (_m *LayeredStoreDatabaseLayer) GetGroupsByTeam(ctx context.Context, teamId string, page int, perPage int, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
// GetGroupsByTeam provides a mock function with given fields: ctx, teamId, opts, hints
func (_m *LayeredStoreDatabaseLayer) GetGroupsByTeam(ctx context.Context, teamId string, opts model.GroupSearchOpts, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, teamId, page, perPage)
_ca = append(_ca, ctx, teamId, opts)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, string, int, int, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, teamId, page, perPage, hints...)
if rf, ok := ret.Get(0).(func(context.Context, string, model.GroupSearchOpts, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, teamId, opts, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)

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

@@ -60,6 +60,52 @@ func (_m *LayeredStoreSupplier) ChannelMembersToRemove(ctx context.Context, hint
return r0
}
// CountGroupsByTeam provides a mock function with given fields: ctx, teamId, opts, hints
func (_m *LayeredStoreSupplier) CountGroupsByTeam(ctx context.Context, teamId string, opts model.GroupSearchOpts, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, teamId, opts)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, string, model.GroupSearchOpts, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, teamId, opts, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// GetGroups provides a mock function with given fields: ctx, page, perPage, opts, hints
func (_m *LayeredStoreSupplier) GetGroups(ctx context.Context, page int, perPage int, opts model.GroupSearchOpts, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, page, perPage, opts)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, int, int, model.GroupSearchOpts, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, page, perPage, opts, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// GetGroupsByChannel provides a mock function with given fields: ctx, channelId, page, perPage, hints
func (_m *LayeredStoreSupplier) GetGroupsByChannel(ctx context.Context, channelId string, page int, perPage int, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
@@ -83,20 +129,20 @@ func (_m *LayeredStoreSupplier) GetGroupsByChannel(ctx context.Context, channelI
return r0
}
// GetGroupsByTeam provides a mock function with given fields: ctx, teamId, page, perPage, hints
func (_m *LayeredStoreSupplier) GetGroupsByTeam(ctx context.Context, teamId string, page int, perPage int, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
// GetGroupsByTeam provides a mock function with given fields: ctx, teamId, opts, hints
func (_m *LayeredStoreSupplier) GetGroupsByTeam(ctx context.Context, teamId string, opts model.GroupSearchOpts, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, teamId, page, perPage)
_ca = append(_ca, ctx, teamId, opts)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, string, int, int, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, teamId, page, perPage, hints...)
if rf, ok := ret.Get(0).(func(context.Context, string, model.GroupSearchOpts, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, teamId, opts, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)

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

@@ -510,7 +510,6 @@ func TestMergeWithSlices(t *testing.T) {
merged, err := mergeStringSlices(m1, m2)
require.NoError(t, err)
fmt.Println("expeted: ", expected, " merged: ", merged)
assert.Equal(t, expected, merged)
// of course this won't change merged, even if it did copy... but just in case.
m2 = append(m2, "test")

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

@@ -21,47 +21,50 @@ const (
)
type Params struct {
UserId string
TeamId string
InviteId string
TokenId string
ChannelId string
PostId string
FileId string
Filename string
PluginId string
CommandId string
HookId string
ReportId string
EmojiId string
AppId string
Email string
Username string
TeamName string
ChannelName string
PreferenceName string
EmojiName string
Category string
Service string
JobId string
JobType string
ActionId string
RoleId string
RoleName string
SchemeId string
Scope string
GroupId string
Page int
PerPage int
LogsPerPage int
Permanent bool
RemoteId string
SyncableId string
SyncableType model.GroupSyncableType
BotUserId string
Q string
IsLinked *bool
IsConfigured *bool
UserId string
TeamId string
InviteId string
TokenId string
ChannelId string
PostId string
FileId string
Filename string
PluginId string
CommandId string
HookId string
ReportId string
EmojiId string
AppId string
Email string
Username string
TeamName string
ChannelName string
PreferenceName string
EmojiName string
Category string
Service string
JobId string
JobType string
ActionId string
RoleId string
RoleName string
SchemeId string
Scope string
GroupId string
Page int
PerPage int
LogsPerPage int
Permanent bool
RemoteId string
SyncableId string
SyncableType model.GroupSyncableType
BotUserId string
Q string
IsLinked *bool
IsConfigured *bool
NotAssociatedToTeam string
Paginate *bool
IncludeMemberCount bool
}
func ParamsFromRequest(r *http.Request) *Params {
@@ -245,5 +248,15 @@ func ParamsFromRequest(r *http.Request) *Params {
params.IsConfigured = &val
}
params.NotAssociatedToTeam = query.Get("not_associated_to_team")
if val, err := strconv.ParseBool(query.Get("paginate")); err == nil {
params.Paginate = &val
}
if val, err := strconv.ParseBool(query.Get("include_member_count")); err == nil {
params.IncludeMemberCount = val
}
return params
}