[MM-13746] Add GetTeamMembersForUser and GetChannelMembersForUser apis (#10269)

* Add GetTeamMembersForUser and GetChannelMembersForUser apis

* Address comments

* Fix tests

* Fix test

* Fix comment

* Fix minimum server version

* Change to []*model.ChannelMember

* Fix panic, add more tests

* Remove print statement
This commit is contained in:
Shobhit Gupta 2019-02-23 11:41:19 -08:00 committed by Lev
parent 0e50ec6a35
commit b4d645f121
15 changed files with 373 additions and 0 deletions

View file

@ -1207,6 +1207,22 @@ func (a *App) GetChannelMembersForUser(teamId string, userId string) (*model.Cha
return result.Data.(*model.ChannelMembers), nil
}
func (a *App) GetChannelMembersForUserWithPagination(teamId, userId string, page, perPage int) ([]*model.ChannelMember, *model.AppError) {
result := <-a.Srv.Store.Channel().GetMembersForUserWithPagination(teamId, userId, page, perPage)
if result.Err != nil {
return nil, result.Err
}
m := result.Data.(*model.ChannelMembers)
members := make([]*model.ChannelMember, 0)
if m != nil {
for _, member := range *m {
members = append(members, &member)
}
}
return members, nil
}
func (a *App) GetChannelMemberCount(channelId string) (int64, *model.AppError) {
result := <-a.Srv.Store.Channel().GetMemberCount(channelId, true)
if result.Err != nil {

View file

@ -158,6 +158,10 @@ func (api *PluginAPI) GetTeamMember(teamId, userId string) (*model.TeamMember, *
return api.app.GetTeamMember(teamId, userId)
}
func (api *PluginAPI) GetTeamMembersForUser(userId string, page int, perPage int) ([]*model.TeamMember, *model.AppError) {
return api.app.GetTeamMembersForUserWithPagination(userId, page, perPage)
}
func (api *PluginAPI) UpdateTeamMemberRoles(teamId, userId, newRoles string) (*model.TeamMember, *model.AppError) {
return api.app.UpdateTeamMemberRoles(teamId, userId, newRoles)
}
@ -375,6 +379,10 @@ func (api *PluginAPI) GetChannelMembersByIds(channelId string, userIds []string)
return api.app.GetChannelMembersByIds(channelId, userIds)
}
func (api *PluginAPI) GetChannelMembersForUser(teamId, userId string, page, perPage int) ([]*model.ChannelMember, *model.AppError) {
return api.app.GetChannelMembersForUserWithPagination(teamId, userId, page, perPage)
}
func (api *PluginAPI) UpdateChannelMemberRoles(channelId, userId, newRoles string) (*model.ChannelMember, *model.AppError) {
return api.app.UpdateChannelMemberRoles(channelId, userId, newRoles)
}

View file

@ -881,3 +881,30 @@ func TestPluginAPI_SearchTeams(t *testing.T) {
assert.Empty(t, teams)
})
}
func TestPluginAPI_GetTeamMembersForUser(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
api := th.SetupPluginAPI()
userId := th.BasicUser.Id
teamMembers, err := api.GetTeamMembersForUser(userId, 0, 10)
assert.Nil(t, err)
assert.Equal(t, len(teamMembers), 1)
assert.Equal(t, teamMembers[0].TeamId, th.BasicTeam.Id)
assert.Equal(t, teamMembers[0].UserId, th.BasicUser.Id)
}
func TestPluginAPI_GetChannelMembersForUser(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
api := th.SetupPluginAPI()
userId := th.BasicUser.Id
teamId := th.BasicTeam.Id
channelMembers, err := api.GetChannelMembersForUser(teamId, userId, 0, 10)
assert.Nil(t, err)
assert.Equal(t, len(channelMembers), 3)
assert.Equal(t, channelMembers[0].UserId, th.BasicUser.Id)
}

View file

@ -590,6 +590,14 @@ func (a *App) GetTeamMembersForUser(userId string) ([]*model.TeamMember, *model.
return result.Data.([]*model.TeamMember), nil
}
func (a *App) GetTeamMembersForUserWithPagination(userId string, page, perPage int) ([]*model.TeamMember, *model.AppError) {
result := <-a.Srv.Store.Team().GetTeamsForUserWithPagination(userId, page, perPage)
if result.Err != nil {
return nil, result.Err
}
return result.Data.([]*model.TeamMember), nil
}
func (a *App) GetTeamMembers(teamId string, offset int, limit int) ([]*model.TeamMember, *model.AppError) {
result := <-a.Srv.Store.Team().GetMembers(teamId, offset, limit)
if result.Err != nil {

View file

@ -174,6 +174,11 @@ type API interface {
// GetTeamMember returns a specific membership.
GetTeamMember(teamId, userId string) (*model.TeamMember, *model.AppError)
// GetTeamMembersForUser returns all team memberships for a user.
//
// Minimum server version: 5.10
GetTeamMembersForUser(userId string, page int, perPage int) ([]*model.TeamMember, *model.AppError)
// UpdateTeamMemberRoles updates the role for a team membership.
UpdateTeamMemberRoles(teamId, userId, newRoles string) (*model.TeamMember, *model.AppError)
@ -247,6 +252,11 @@ type API interface {
// Minimum server version: 5.6
GetChannelMembersByIds(channelId string, userIds []string) (*model.ChannelMembers, *model.AppError)
// GetChannelMembersForUser returns all channel memberships on a team for a user.
//
// Minimum server version: 5.10
GetChannelMembersForUser(teamId, userId string, page, perPage int) ([]*model.ChannelMember, *model.AppError)
// UpdateChannelMemberRoles updates a user's roles for a channel.
UpdateChannelMemberRoles(channelId, userId, newRoles string) (*model.ChannelMember, *model.AppError)

View file

@ -1636,6 +1636,37 @@ func (s *apiRPCServer) GetTeamMember(args *Z_GetTeamMemberArgs, returns *Z_GetTe
return nil
}
type Z_GetTeamMembersForUserArgs struct {
A string
B int
C int
}
type Z_GetTeamMembersForUserReturns struct {
A []*model.TeamMember
B *model.AppError
}
func (g *apiRPCClient) GetTeamMembersForUser(userId string, page int, perPage int) ([]*model.TeamMember, *model.AppError) {
_args := &Z_GetTeamMembersForUserArgs{userId, page, perPage}
_returns := &Z_GetTeamMembersForUserReturns{}
if err := g.client.Call("Plugin.GetTeamMembersForUser", _args, _returns); err != nil {
log.Printf("RPC call to GetTeamMembersForUser API failed: %s", err.Error())
}
return _returns.A, _returns.B
}
func (s *apiRPCServer) GetTeamMembersForUser(args *Z_GetTeamMembersForUserArgs, returns *Z_GetTeamMembersForUserReturns) error {
if hook, ok := s.impl.(interface {
GetTeamMembersForUser(userId string, page int, perPage int) ([]*model.TeamMember, *model.AppError)
}); ok {
returns.A, returns.B = hook.GetTeamMembersForUser(args.A, args.B, args.C)
} else {
return encodableError(fmt.Errorf("API GetTeamMembersForUser called but not implemented."))
}
return nil
}
type Z_UpdateTeamMemberRolesArgs struct {
A string
B string
@ -2204,6 +2235,38 @@ func (s *apiRPCServer) GetChannelMembersByIds(args *Z_GetChannelMembersByIdsArgs
return nil
}
type Z_GetChannelMembersForUserArgs struct {
A string
B string
C int
D int
}
type Z_GetChannelMembersForUserReturns struct {
A []*model.ChannelMember
B *model.AppError
}
func (g *apiRPCClient) GetChannelMembersForUser(teamId, userId string, page, perPage int) ([]*model.ChannelMember, *model.AppError) {
_args := &Z_GetChannelMembersForUserArgs{teamId, userId, page, perPage}
_returns := &Z_GetChannelMembersForUserReturns{}
if err := g.client.Call("Plugin.GetChannelMembersForUser", _args, _returns); err != nil {
log.Printf("RPC call to GetChannelMembersForUser API failed: %s", err.Error())
}
return _returns.A, _returns.B
}
func (s *apiRPCServer) GetChannelMembersForUser(args *Z_GetChannelMembersForUserArgs, returns *Z_GetChannelMembersForUserReturns) error {
if hook, ok := s.impl.(interface {
GetChannelMembersForUser(teamId, userId string, page, perPage int) ([]*model.ChannelMember, *model.AppError)
}); ok {
returns.A, returns.B = hook.GetChannelMembersForUser(args.A, args.B, args.C, args.D)
} else {
return encodableError(fmt.Errorf("API GetChannelMembersForUser called but not implemented."))
}
return nil
}
type Z_UpdateChannelMemberRolesArgs struct {
A string
B string

View file

@ -515,6 +515,31 @@ func (_m *API) GetChannelMembersByIds(channelId string, userIds []string) (*mode
return r0, r1
}
// GetChannelMembersForUser provides a mock function with given fields: teamId, userId, page, perPage
func (_m *API) GetChannelMembersForUser(teamId string, userId string, page int, perPage int) ([]*model.ChannelMember, *model.AppError) {
ret := _m.Called(teamId, userId, page, perPage)
var r0 []*model.ChannelMember
if rf, ok := ret.Get(0).(func(string, string, int, int) []*model.ChannelMember); ok {
r0 = rf(teamId, userId, page, perPage)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.ChannelMember)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(string, string, int, int) *model.AppError); ok {
r1 = rf(teamId, userId, page, perPage)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// GetChannelStats provides a mock function with given fields: channelId
func (_m *API) GetChannelStats(channelId string) (*model.ChannelStats, *model.AppError) {
ret := _m.Called(channelId)
@ -1291,6 +1316,31 @@ func (_m *API) GetTeamMembers(teamId string, page int, perPage int) ([]*model.Te
return r0, r1
}
// GetTeamMembersForUser provides a mock function with given fields: userId, page, perPage
func (_m *API) GetTeamMembersForUser(userId string, page int, perPage int) ([]*model.TeamMember, *model.AppError) {
ret := _m.Called(userId, page, perPage)
var r0 []*model.TeamMember
if rf, ok := ret.Get(0).(func(string, int, int) []*model.TeamMember); ok {
r0 = rf(userId, page, perPage)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.TeamMember)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(string, int, int) *model.AppError); ok {
r1 = rf(userId, page, perPage)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// GetTeamStats provides a mock function with given fields: teamId
func (_m *API) GetTeamStats(teamId string) (*model.TeamStats, *model.AppError) {
ret := _m.Called(teamId)

View file

@ -1923,6 +1923,21 @@ func (s SqlChannelStore) GetMembersForUser(teamId string, userId string) store.S
})
}
func (s SqlChannelStore) GetMembersForUserWithPagination(teamId, userId string, page, perPage int) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var dbMembers channelMemberWithSchemeRolesList
offset := page * perPage
_, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.UserId = :UserId Limit :Limit Offset :Offset", map[string]interface{}{"TeamId": teamId, "UserId": userId, "Limit": perPage, "Offset": offset})
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetMembersForUserWithPagination", "store.sql_channel.get_members.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError)
return
}
result.Data = dbMembers.ToModel()
})
}
func (s SqlChannelStore) AutocompleteInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
deleteFilter := "AND c.DeleteAt = 0"

View file

@ -670,6 +670,20 @@ func (s SqlTeamStore) GetTeamsForUser(userId string) store.StoreChannel {
})
}
func (s SqlTeamStore) GetTeamsForUserWithPagination(userId string, page, perPage int) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var dbMembers teamMemberWithSchemeRolesList
offset := page * perPage
_, err := s.GetReplica().Select(&dbMembers, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.UserId = :UserId Limit :Limit Offset :Offset", map[string]interface{}{"UserId": userId, "Limit": perPage, "Offset": offset})
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetTeamsForUserWithPagination", "store.sql_team.get_members.app_error", nil, "userId="+userId+" "+err.Error(), http.StatusInternalServerError)
return
}
result.Data = dbMembers.ToModel()
})
}
func (s SqlTeamStore) GetChannelUnreadsForAllTeams(excludeTeamId, userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var data []*model.ChannelUnread

View file

@ -104,6 +104,7 @@ type TeamStore interface {
GetTotalMemberCount(teamId string) StoreChannel
GetActiveMemberCount(teamId string) StoreChannel
GetTeamsForUser(userId string) StoreChannel
GetTeamsForUserWithPagination(userId string, page, perPage int) StoreChannel
GetChannelUnreadsForAllTeams(excludeTeamId, userId string) StoreChannel
GetChannelUnreadsForTeam(teamId, userId string) StoreChannel
RemoveMember(teamId string, userId string) StoreChannel
@ -169,6 +170,7 @@ type ChannelStore interface {
IncrementMentionCount(channelId string, userId string) StoreChannel
AnalyticsTypeCount(teamId string, channelType string) StoreChannel
GetMembersForUser(teamId string, userId string) StoreChannel
GetMembersForUserWithPagination(teamId, userId string, page, perPage int) StoreChannel
AutocompleteInTeam(teamId string, term string, includeDeleted bool) StoreChannel
AutocompleteInTeamForSearch(teamId string, userId string, term string, includeDeleted bool) StoreChannel
SearchAllChannels(term string, includeDeleted bool) StoreChannel

View file

@ -57,6 +57,7 @@ func TestChannelStore(t *testing.T, ss store.Store, s SqlSupplier) {
t.Run("GetPublicChannelsByIdsForTeam", func(t *testing.T) { testChannelStoreGetPublicChannelsByIdsForTeam(t, ss) })
t.Run("GetChannelCounts", func(t *testing.T) { testChannelStoreGetChannelCounts(t, ss) })
t.Run("GetMembersForUser", func(t *testing.T) { testChannelStoreGetMembersForUser(t, ss) })
t.Run("GetMembersForUserWithPagination", func(t *testing.T) { testChannelStoreGetMembersForUserWithPagination(t, ss) })
t.Run("UpdateLastViewedAt", func(t *testing.T) { testChannelStoreUpdateLastViewedAt(t, ss) })
t.Run("IncrementMentionCount", func(t *testing.T) { testChannelStoreIncrementMentionCount(t, ss) })
t.Run("UpdateChannelMember", func(t *testing.T) { testUpdateChannelMember(t, ss) })
@ -1419,6 +1420,50 @@ func testChannelStoreGetMembersForUser(t *testing.T, ss store.Store) {
}
}
func testChannelStoreGetMembersForUserWithPagination(t *testing.T, ss store.Store) {
t1 := model.Team{}
t1.DisplayName = "Name"
t1.Name = model.NewId()
t1.Email = MakeEmail()
t1.Type = model.TEAM_OPEN
store.Must(ss.Team().Save(&t1))
o1 := model.Channel{}
o1.TeamId = t1.Id
o1.DisplayName = "Channel1"
o1.Name = "zz" + model.NewId() + "b"
o1.Type = model.CHANNEL_OPEN
store.Must(ss.Channel().Save(&o1, -1))
o2 := model.Channel{}
o2.TeamId = o1.TeamId
o2.DisplayName = "Channel2"
o2.Name = "zz" + model.NewId() + "b"
o2.Type = model.CHANNEL_OPEN
store.Must(ss.Channel().Save(&o2, -1))
m1 := model.ChannelMember{}
m1.ChannelId = o1.Id
m1.UserId = model.NewId()
m1.NotifyProps = model.GetDefaultChannelNotifyProps()
store.Must(ss.Channel().SaveMember(&m1))
m2 := model.ChannelMember{}
m2.ChannelId = o2.Id
m2.UserId = m1.UserId
m2.NotifyProps = model.GetDefaultChannelNotifyProps()
store.Must(ss.Channel().SaveMember(&m2))
cresult := <-ss.Channel().GetMembersForUserWithPagination(o1.TeamId, m1.UserId, 0, 1)
members := cresult.Data.(*model.ChannelMembers)
assert.Len(t, *members, 1)
cresult = <-ss.Channel().GetMembersForUserWithPagination(o1.TeamId, m1.UserId, 1, 1)
members = cresult.Data.(*model.ChannelMembers)
assert.Len(t, *members, 1)
}
func testChannelStoreUpdateLastViewedAt(t *testing.T, ss store.Store) {
o1 := model.Channel{}
o1.TeamId = model.NewId()

View file

@ -544,6 +544,22 @@ func (_m *ChannelStore) GetMembersForUser(teamId string, userId string) store.St
return r0
}
// GetMembersForUserWithPagination provides a mock function with given fields: teamId, userId, page, perPage
func (_m *ChannelStore) GetMembersForUserWithPagination(teamId string, userId string, page int, perPage int) store.StoreChannel {
ret := _m.Called(teamId, userId, page, perPage)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, string, int, int) store.StoreChannel); ok {
r0 = rf(teamId, userId, page, perPage)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// GetMoreChannels provides a mock function with given fields: teamId, userId, offset, limit
func (_m *ChannelStore) GetMoreChannels(teamId string, userId string, offset int, limit int) store.StoreChannel {
ret := _m.Called(teamId, userId, offset, limit)

View file

@ -0,0 +1,26 @@
// Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`.
package mocks
import mock "github.com/stretchr/testify/mock"
// JSONSerializable is an autogenerated mock type for the JSONSerializable type
type JSONSerializable struct {
mock.Mock
}
// ToJson provides a mock function with given fields:
func (_m *JSONSerializable) ToJson() string {
ret := _m.Called()
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}

View file

@ -349,6 +349,22 @@ func (_m *TeamStore) GetTeamsForUser(userId string) store.StoreChannel {
return r0
}
// GetTeamsForUserWithPagination provides a mock function with given fields: userId, page, perPage
func (_m *TeamStore) GetTeamsForUserWithPagination(userId string, page int, perPage int) store.StoreChannel {
ret := _m.Called(userId, page, perPage)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, int, int) store.StoreChannel); ok {
r0 = rf(userId, page, perPage)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// GetTotalMemberCount provides a mock function with given fields: teamId
func (_m *TeamStore) GetTotalMemberCount(teamId string) store.StoreChannel {
ret := _m.Called(teamId)

View file

@ -47,6 +47,7 @@ func TestTeamStore(t *testing.T, ss store.Store) {
t.Run("AnalyticsGetTeamCountForScheme", func(t *testing.T) { testTeamStoreAnalyticsGetTeamCountForScheme(t, ss) })
t.Run("GetAllForExportAfter", func(t *testing.T) { testTeamStoreGetAllForExportAfter(t, ss) })
t.Run("GetTeamMembersForExport", func(t *testing.T) { testTeamStoreGetTeamMembersForExport(t, ss) })
t.Run("GetTeamsForUserWithPagination", func(t *testing.T) { testTeamMembersWithPagination(t, ss) })
}
func testTeamStoreSave(t *testing.T, ss store.Store) {
@ -668,6 +669,62 @@ func testTeamMembers(t *testing.T, ss store.Store) {
}
}
func testTeamMembersWithPagination(t *testing.T, ss store.Store) {
teamId1 := model.NewId()
teamId2 := model.NewId()
m1 := &model.TeamMember{TeamId: teamId1, UserId: model.NewId()}
m2 := &model.TeamMember{TeamId: teamId1, UserId: model.NewId()}
m3 := &model.TeamMember{TeamId: teamId2, UserId: model.NewId()}
r1 := <-ss.Team().SaveMember(m1, -1)
require.Nil(t, r1.Err)
store.Must(ss.Team().SaveMember(m2, -1))
store.Must(ss.Team().SaveMember(m3, -1))
r1 = <-ss.Team().GetTeamsForUserWithPagination(m1.UserId, 0, 1)
require.Nil(t, r1.Err)
ms := r1.Data.([]*model.TeamMember)
require.Len(t, ms, 1)
require.Equal(t, m1.TeamId, ms[0].TeamId)
r1 = <-ss.Team().RemoveMember(teamId1, m1.UserId)
require.Nil(t, r1.Err)
r1 = <-ss.Team().GetMembers(teamId1, 0, 100)
require.Nil(t, r1.Err)
ms = r1.Data.([]*model.TeamMember)
require.Len(t, ms, 1)
require.Equal(t, m2.UserId, ms[0].UserId)
store.Must(ss.Team().SaveMember(m1, -1))
r1 = <-ss.Team().RemoveAllMembersByTeam(teamId1)
require.Nil(t, r1.Err)
uid := model.NewId()
m4 := &model.TeamMember{TeamId: teamId1, UserId: uid}
m5 := &model.TeamMember{TeamId: teamId2, UserId: uid}
store.Must(ss.Team().SaveMember(m4, -1))
store.Must(ss.Team().SaveMember(m5, -1))
r1 = <-ss.Team().GetTeamsForUserWithPagination(uid, 0, 1)
require.Nil(t, r1.Err)
ms = r1.Data.([]*model.TeamMember)
require.Len(t, ms, 1)
r1 = <-ss.Team().RemoveAllMembersByUser(uid)
require.Nil(t, r1.Err)
r1 = <-ss.Team().GetTeamsForUserWithPagination(uid, 1, 1)
require.Nil(t, r1.Err)
ms = r1.Data.([]*model.TeamMember)
require.Len(t, ms, 0)
}
func testSaveTeamMemberMaxMembers(t *testing.T, ss store.Store) {
maxUsersPerTeam := 5