Skip to content

Commit

Permalink
Merge 075c2c2 into 060b9f9
Browse files Browse the repository at this point in the history
  • Loading branch information
grundleborg committed Mar 29, 2017
2 parents 060b9f9 + 075c2c2 commit a2a56c2
Show file tree
Hide file tree
Showing 7 changed files with 304 additions and 0 deletions.
13 changes: 13 additions & 0 deletions api4/user.go
Expand Up @@ -264,6 +264,7 @@ func setProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {

func getUsers(c *Context, w http.ResponseWriter, r *http.Request) {
inTeamId := r.URL.Query().Get("in_team")
notInTeamId := r.URL.Query().Get("not_in_team")
inChannelId := r.URL.Query().Get("in_channel")
notInChannelId := r.URL.Query().Get("not_in_channel")

Expand All @@ -283,6 +284,18 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) {
}

profiles, err = app.GetUsersNotInChannelPage(inTeamId, notInChannelId, c.Params.Page, c.Params.PerPage, c.IsSystemAdmin())
} else if len(notInTeamId) > 0 {
if !app.SessionHasPermissionToTeam(c.Session, notInTeamId, model.PERMISSION_VIEW_TEAM) {
c.SetPermissionError(model.PERMISSION_VIEW_TEAM)
return
}

etag = app.GetUsersNotInTeamEtag(inTeamId)
if HandleEtag(etag, "Get Users Not in Team", w, r) {
return
}

profiles, err = app.GetUsersNotInTeamPage(notInTeamId, c.Params.Page, c.Params.PerPage, c.IsSystemAdmin())
} else if len(inTeamId) > 0 {
if !app.SessionHasPermissionToTeam(c.Session, inTeamId, model.PERMISSION_VIEW_TEAM) {
c.SetPermissionError(model.PERMISSION_VIEW_TEAM)
Expand Down
46 changes: 46 additions & 0 deletions api4/user_test.go
Expand Up @@ -883,6 +883,52 @@ func TestGetUsersInTeam(t *testing.T) {
CheckNoError(t, resp)
}

func TestGetUsersNotInTeam(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
defer TearDown()
Client := th.Client
teamId := th.BasicTeam.Id

rusers, resp := Client.GetUsersNotInTeam(teamId, 0, 60, "")
CheckNoError(t, resp)
for _, u := range rusers {
CheckUserSanitization(t, u)
}

rusers, resp = Client.GetUsersNotInTeam(teamId, 0, 60, resp.Etag)
CheckEtag(t, rusers, resp)

rusers, resp = Client.GetUsersNotInTeam(teamId, 0, 1, "")
CheckNoError(t, resp)
if len(rusers) != 1 {
t.Fatal("should be 1 per page")
}

rusers, resp = Client.GetUsersNotInTeam(teamId, 1, 1, "")
CheckNoError(t, resp)
if len(rusers) != 1 {
t.Fatal("should be 1 per page")
}

rusers, resp = Client.GetUsersNotInTeam(teamId, 10000, 100, "")
CheckNoError(t, resp)
if len(rusers) != 0 {
t.Fatal("should be no users")
}

Client.Logout()
_, resp = Client.GetUsersNotInTeam(teamId, 0, 60, "")
CheckUnauthorizedStatus(t, resp)

user := th.CreateUser()
Client.Login(user.Email, user.Password)
_, resp = Client.GetUsersNotInTeam(teamId, 0, 60, "")
CheckForbiddenStatus(t, resp)

_, resp = th.SystemAdminClient.GetUsersNotInTeam(teamId, 0, 60, "")
CheckNoError(t, resp)
}

func TestGetUsersInChannel(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
defer TearDown()
Expand Down
25 changes: 25 additions & 0 deletions app/user.go
Expand Up @@ -434,6 +434,14 @@ func GetUsersInTeam(teamId string, offset int, limit int) ([]*model.User, *model
}
}

func GetUsersNotInTeam(teamId string, offset int, limit int) ([]*model.User, *model.AppError) {
if result := <-Srv.Store.User().GetProfilesNotInTeam(teamId, offset, limit); result.Err != nil {
return nil, result.Err
} else {
return result.Data.([]*model.User), nil
}
}

func GetUsersInTeamMap(teamId string, offset int, limit int, asAdmin bool) (map[string]*model.User, *model.AppError) {
users, err := GetUsersInTeam(teamId, offset, limit)
if err != nil {
Expand Down Expand Up @@ -463,10 +471,27 @@ func GetUsersInTeamPage(teamId string, page int, perPage int, asAdmin bool) ([]*
return users, nil
}

func GetUsersNotInTeamPage(teamId string, page int, perPage int, asAdmin bool) ([]*model.User, *model.AppError) {
users, err := GetUsersNotInTeam(teamId, page*perPage, perPage)
if err != nil {
return nil, err
}

for _, user := range users {
SanitizeProfile(user, asAdmin)
}

return users, nil
}

func GetUsersInTeamEtag(teamId string) string {
return (<-Srv.Store.User().GetEtagForProfiles(teamId)).Data.(string)
}

func GetUsersNotInTeamEtag(teamId string) string {
return (<-Srv.Store.User().GetEtagForProfilesNotInTeam(teamId)).Data.(string)
}

func GetUsersInChannel(channelId string, offset int, limit int) ([]*model.User, *model.AppError) {
if result := <-Srv.Store.User().GetProfilesInChannel(channelId, offset, limit); result.Err != nil {
return nil, result.Err
Expand Down
11 changes: 11 additions & 0 deletions model/client4.go
Expand Up @@ -467,6 +467,17 @@ func (c *Client4) GetUsersInTeam(teamId string, page int, perPage int, etag stri
}
}

// GetUsersNotInTeam returns a page of users who are not in a team. Page counting starts at 0.
func (c *Client4) GetUsersNotInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) {
query := fmt.Sprintf("?not_in_team=%v&page=%v&per_page=%v", teamId, page, perPage)
if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil {
return nil, &Response{StatusCode: r.StatusCode, Error: err}
} else {
defer closeBody(r)
return UserListFromJson(r.Body), BuildResponse(r)
}
}

// GetUsersInChannel returns a page of users on a team. Page counting starts at 0.
func (c *Client4) GetUsersInChannel(channelId string, page int, perPage int, etag string) ([]*User, *Response) {
query := fmt.Sprintf("?in_channel=%v&page=%v&per_page=%v", channelId, page, perPage)
Expand Down
73 changes: 73 additions & 0 deletions store/sql_user_store.go
Expand Up @@ -1514,3 +1514,76 @@ func (us SqlUserStore) AnalyticsGetSystemAdminCount() StoreChannel {

return storeChannel
}

func (us SqlUserStore) GetProfilesNotInTeam(teamId string, offset int, limit int) StoreChannel {

storeChannel := make(StoreChannel)

go func() {
result := StoreResult{}

var users []*model.User

if _, err := us.GetReplica().Select(&users, `
SELECT
u.*
FROM Users u
LEFT JOIN TeamMembers tm
ON tm.UserId = u.Id
AND tm.TeamId = :TeamId
AND tm.DeleteAt = 0
WHERE tm.UserId IS NULL
ORDER BY u.Username ASC
LIMIT :Limit OFFSET :Offset
`, map[string]interface{}{"TeamId": teamId, "Offset": offset, "Limit": limit}); err != nil {
result.Err = model.NewLocAppError("SqlUserStore.GetProfilesNotInTeam", "store.sql_user.get_profiles.app_error", nil, err.Error())
} else {

for _, u := range users {
u.Password = ""
u.AuthData = new(string)
*u.AuthData = ""
}

result.Data = users
}

storeChannel <- result
close(storeChannel)
}()

return storeChannel
}

func (us SqlUserStore) GetEtagForProfilesNotInTeam(teamId string) StoreChannel {

storeChannel := make(StoreChannel)

go func() {
result := StoreResult{}

updateAt, err := us.GetReplica().SelectInt(`
SELECT
u.UpdateAt
FROM Users u
LEFT JOIN TeamMembers tm
ON tm.UserId = u.Id
AND tm.TeamId = :TeamId
AND tm.DeleteAt = 0
WHERE tm.UserId IS NULL
ORDER BY u.UpdateAt DESC
LIMIT 1
`, map[string]interface{}{"TeamId": teamId})

if err != nil {
result.Data = fmt.Sprintf("%v.%v.%v.%v", model.CurrentVersion, model.GetMillis(), utils.Cfg.PrivacySettings.ShowFullName, utils.Cfg.PrivacySettings.ShowEmailAddress)
} else {
result.Data = fmt.Sprintf("%v.%v.%v.%v", model.CurrentVersion, updateAt, utils.Cfg.PrivacySettings.ShowFullName, utils.Cfg.PrivacySettings.ShowEmailAddress)
}

storeChannel <- result
close(storeChannel)
}()

return storeChannel
}
134 changes: 134 additions & 0 deletions store/sql_user_store_test.go
Expand Up @@ -1632,3 +1632,137 @@ func TestUserStoreAnalyticsGetSystemAdminCount(t *testing.T) {
}
}
}

func TestUserStoreGetProfilesNotInTeam(t *testing.T) {
Setup()

teamId := model.NewId()

u1 := &model.User{}
u1.Email = model.NewId()
Must(store.User().Save(u1))
Must(store.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id}))
Must(store.User().UpdateUpdateAt(u1.Id))

u2 := &model.User{}
u2.Email = model.NewId()
Must(store.User().Save(u2))
Must(store.User().UpdateUpdateAt(u2.Id))

var initialUsersNotInTeam int
var etag1, etag2, etag3 string

if er1 := <-store.User().GetEtagForProfilesNotInTeam(teamId); er1.Err != nil {
t.Fatal(er1.Err)
} else {
etag1 = er1.Data.(string)
}

if r1 := <-store.User().GetProfilesNotInTeam(teamId, 0, 100000); r1.Err != nil {
t.Fatal(r1.Err)
} else {
users := r1.Data.([]*model.User)
initialUsersNotInTeam = len(users)
if initialUsersNotInTeam < 1 {
t.Fatalf("Should be at least 1 user not in the team")
}

found := false
for _, u := range users {
if u.Id == u2.Id {
found = true
}
if u.Id == u1.Id {
t.Fatalf("Should not have found user1")
}
}

if !found {
t.Fatal("missing user2")
}
}

time.Sleep(time.Millisecond * 10)
Must(store.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u2.Id}))
Must(store.User().UpdateUpdateAt(u2.Id))

if er2 := <-store.User().GetEtagForProfilesNotInTeam(teamId); er2.Err != nil {
t.Fatal(er2.Err)
} else {
etag2 = er2.Data.(string)
if etag1 == etag2 {
t.Fatalf("etag should have changed")
}
}

if r2 := <-store.User().GetProfilesNotInTeam(teamId, 0, 100000); r2.Err != nil {
t.Fatal(r2.Err)
} else {
users := r2.Data.([]*model.User)

if len(users) != initialUsersNotInTeam-1 {
t.Fatalf("Should be one less user not in team")
}

for _, u := range users {
if u.Id == u2.Id {
t.Fatalf("Should not have found user2")
}
if u.Id == u1.Id {
t.Fatalf("Should not have found user1")
}
}
}

time.Sleep(time.Millisecond * 10)
Must(store.Team().RemoveMember(teamId, u1.Id))
Must(store.Team().RemoveMember(teamId, u2.Id))
Must(store.User().UpdateUpdateAt(u1.Id))
Must(store.User().UpdateUpdateAt(u2.Id))

if er3 := <-store.User().GetEtagForProfilesNotInTeam(teamId); er3.Err != nil {
t.Fatal(er3.Err)
} else {
etag3 = er3.Data.(string)
t.Log(etag3)
if etag1 == etag3 || etag3 == etag2 {
t.Fatalf("etag should have changed")
}
}

if r3 := <-store.User().GetProfilesNotInTeam(teamId, 0, 100000); r3.Err != nil {
t.Fatal(r3.Err)
} else {
users := r3.Data.([]*model.User)
found1, found2 := false, false
for _, u := range users {
if u.Id == u2.Id {
found2 = true
}
if u.Id == u1.Id {
found1 = true
}
}

if !found1 || !found2 {
t.Fatal("missing user1 or user2")
}
}

time.Sleep(time.Millisecond * 10)
u3 := &model.User{}
u3.Email = model.NewId()
Must(store.User().Save(u3))
Must(store.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u3.Id}))
Must(store.User().UpdateUpdateAt(u3.Id))

if er4 := <-store.User().GetEtagForProfilesNotInTeam(teamId); er4.Err != nil {
t.Fatal(er4.Err)
} else {
etag4 := er4.Data.(string)
t.Log(etag4)
if etag4 != etag3 {
t.Fatalf("etag should be the same")
}
}
}
2 changes: 2 additions & 0 deletions store/store.go
Expand Up @@ -205,6 +205,8 @@ type UserStore interface {
SearchWithoutTeam(term string, options map[string]bool) StoreChannel
AnalyticsGetInactiveUsersCount() StoreChannel
AnalyticsGetSystemAdminCount() StoreChannel
GetProfilesNotInTeam(teamId string, offset int, limit int) StoreChannel
GetEtagForProfilesNotInTeam(teamId string) StoreChannel
}

type SessionStore interface {
Expand Down

0 comments on commit a2a56c2

Please sign in to comment.