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.
This commit is contained in:
Martin Kraft authored and GitHub committed 2019-05-10 11:47:21 -04:00
1 parent 74c7c46a7d
commit 480fffd3cc
24 files changed
+1001 -146

No files matched your search

+91 -9
View File
@@ -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)
}
+111 -10
View File
@@ -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)
}
+1 -1
View File
@@ -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 {
+18 -2
View File
@@ -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
}
+12 -2
View File
@@ -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)
}
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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
}
+1 -1
View File
@@ -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
}
+4
View File
@@ -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"
+34 -4
View File
@@ -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)
+14 -1
View File
@@ -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
+14 -2
View File
@@ -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)
})
}
+3 -1
View File
@@ -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
}
+10 -2
View File
@@ -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...)
}
+12 -2
View File
@@ -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...)
}
+155 -25
View File
@@ -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
}
+3 -1
View File
@@ -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 {
-2
View File
@@ -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)
+322 -21
View File
@@ -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))
})
}
}
+37 -5
View File
@@ -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)
+51 -5
View File
@@ -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)
-1
View File
@@ -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")
+54 -41
View File
@@ -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
}