summaryrefslogtreecommitdiffstats
path: root/model
diff options
context:
space:
mode:
authorJesse Hallam <jesse.hallam@gmail.com>2018-10-17 11:24:12 -0400
committerHarrison Healey <harrisonmhealey@gmail.com>2018-10-17 11:24:12 -0400
commit715097cc76510a3d78ba83e8544ee7c956ed26e9 (patch)
tree0b6d41e88bf75ad34c585d9db80f04cf8d780338 /model
parente8c9ccaa7e47f1cba3d2b126f6ebbb092fa43235 (diff)
downloadchat-715097cc76510a3d78ba83e8544ee7c956ed26e9.tar.gz
chat-715097cc76510a3d78ba83e8544ee7c956ed26e9.tar.bz2
chat-715097cc76510a3d78ba83e8544ee7c956ed26e9.zip
MM-12234: configurable limit to user autocomplete and search matches (#9499)
* unit test cleanup * allow limiting user search results * clean up test users before starting * model UserSearchOptions to simplify parameters
Diffstat (limited to 'model')
-rw-r--r--model/client4.go28
-rw-r--r--model/user_search.go29
-rw-r--r--model/user_search_test.go4
3 files changed, 49 insertions, 12 deletions
diff --git a/model/client4.go b/model/client4.go
index c35dab904..c5001c048 100644
--- a/model/client4.go
+++ b/model/client4.go
@@ -417,6 +417,10 @@ func (c *Client4) DoApiPost(url string, data string) (*http.Response, *AppError)
return c.DoApiRequest(http.MethodPost, c.ApiUrl+url, data, "")
}
+func (c *Client4) doApiPostBytes(url string, data []byte) (*http.Response, *AppError) {
+ return c.doApiRequestBytes(http.MethodPost, c.ApiUrl+url, data, "")
+}
+
func (c *Client4) DoApiPut(url string, data string) (*http.Response, *AppError) {
return c.DoApiRequest(http.MethodPut, c.ApiUrl+url, data, "")
}
@@ -426,7 +430,15 @@ func (c *Client4) DoApiDelete(url string) (*http.Response, *AppError) {
}
func (c *Client4) DoApiRequest(method, url, data, etag string) (*http.Response, *AppError) {
- rq, _ := http.NewRequest(method, url, strings.NewReader(data))
+ return c.doApiRequestReader(method, url, strings.NewReader(data), etag)
+}
+
+func (c *Client4) doApiRequestBytes(method, url string, data []byte, etag string) (*http.Response, *AppError) {
+ return c.doApiRequestReader(method, url, bytes.NewReader(data), etag)
+}
+
+func (c *Client4) doApiRequestReader(method, url string, data io.Reader, etag string) (*http.Response, *AppError) {
+ rq, _ := http.NewRequest(method, url, data)
if len(etag) > 0 {
rq.Header.Set(HEADER_ETAG_CLIENT, etag)
@@ -691,8 +703,8 @@ func (c *Client4) GetUserByEmail(email, etag string) (*User, *Response) {
}
// AutocompleteUsersInTeam returns the users on a team based on search term.
-func (c *Client4) AutocompleteUsersInTeam(teamId string, username string, etag string) (*UserAutocomplete, *Response) {
- query := fmt.Sprintf("?in_team=%v&name=%v", teamId, username)
+func (c *Client4) AutocompleteUsersInTeam(teamId string, username string, limit int, etag string) (*UserAutocomplete, *Response) {
+ query := fmt.Sprintf("?in_team=%v&name=%v&limit=%d", teamId, username, limit)
if r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag); err != nil {
return nil, BuildErrorResponse(r, err)
} else {
@@ -702,8 +714,8 @@ func (c *Client4) AutocompleteUsersInTeam(teamId string, username string, etag s
}
// AutocompleteUsersInChannel returns the users in a channel based on search term.
-func (c *Client4) AutocompleteUsersInChannel(teamId string, channelId string, username string, etag string) (*UserAutocomplete, *Response) {
- query := fmt.Sprintf("?in_team=%v&in_channel=%v&name=%v", teamId, channelId, username)
+func (c *Client4) AutocompleteUsersInChannel(teamId string, channelId string, username string, limit int, etag string) (*UserAutocomplete, *Response) {
+ query := fmt.Sprintf("?in_team=%v&in_channel=%v&name=%v&limit=%d", teamId, channelId, username, limit)
if r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag); err != nil {
return nil, BuildErrorResponse(r, err)
} else {
@@ -713,8 +725,8 @@ func (c *Client4) AutocompleteUsersInChannel(teamId string, channelId string, us
}
// AutocompleteUsers returns the users in the system based on search term.
-func (c *Client4) AutocompleteUsers(username string, etag string) (*UserAutocomplete, *Response) {
- query := fmt.Sprintf("?name=%v", username)
+func (c *Client4) AutocompleteUsers(username string, limit int, etag string) (*UserAutocomplete, *Response) {
+ query := fmt.Sprintf("?name=%v&limit=%d", username, limit)
if r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag); err != nil {
return nil, BuildErrorResponse(r, err)
} else {
@@ -875,7 +887,7 @@ func (c *Client4) GetUsersByUsernames(usernames []string) ([]*User, *Response) {
// SearchUsers returns a list of users based on some search criteria.
func (c *Client4) SearchUsers(search *UserSearch) ([]*User, *Response) {
- if r, err := c.DoApiPost(c.GetUsersRoute()+"/search", search.ToJson()); err != nil {
+ if r, err := c.doApiPostBytes(c.GetUsersRoute()+"/search", search.ToJson()); err != nil {
return nil, BuildErrorResponse(r, err)
} else {
defer closeBody(r)
diff --git a/model/user_search.go b/model/user_search.go
index 94596bdcd..68749fbe5 100644
--- a/model/user_search.go
+++ b/model/user_search.go
@@ -8,6 +8,10 @@ import (
"io"
)
+const USER_SEARCH_MAX_LIMIT = 1000
+const USER_SEARCH_DEFAULT_LIMIT = 100
+
+// UserSearch captures the parameters provided by a client for initiating a user search.
type UserSearch struct {
Term string `json:"term"`
TeamId string `json:"team_id"`
@@ -16,17 +20,38 @@ type UserSearch struct {
NotInChannelId string `json:"not_in_channel_id"`
AllowInactive bool `json:"allow_inactive"`
WithoutTeam bool `json:"without_team"`
+ Limit int `json:"limit"`
}
// ToJson convert a User to a json string
-func (u *UserSearch) ToJson() string {
+func (u *UserSearch) ToJson() []byte {
b, _ := json.Marshal(u)
- return string(b)
+ return b
}
// UserSearchFromJson will decode the input and return a User
func UserSearchFromJson(data io.Reader) *UserSearch {
var us *UserSearch
json.NewDecoder(data).Decode(&us)
+
+ if us.Limit == 0 {
+ us.Limit = USER_SEARCH_DEFAULT_LIMIT
+ }
+
return us
}
+
+// UserSearchOptions captures internal parameters derived from the user's permissions and a
+// UserSearch request.
+type UserSearchOptions struct {
+ // IsAdmin tracks whether or not the search is being conducted by an administrator.
+ IsAdmin bool
+ // AllowEmails allows search to examine the emails of users.
+ AllowEmails bool
+ // AllowFullNames allows search to examine the full names of users, vs. just usernames and nicknames.
+ AllowFullNames bool
+ // AllowInactive configures whether or not to return inactive users in the search results.
+ AllowInactive bool
+ // Limit limits the total number of results returned.
+ Limit int
+}
diff --git a/model/user_search_test.go b/model/user_search_test.go
index 0fada7781..89c8b68f7 100644
--- a/model/user_search_test.go
+++ b/model/user_search_test.go
@@ -4,14 +4,14 @@
package model
import (
- "strings"
+ "bytes"
"testing"
)
func TestUserSearchJson(t *testing.T) {
userSearch := UserSearch{Term: NewId(), TeamId: NewId()}
json := userSearch.ToJson()
- ruserSearch := UserSearchFromJson(strings.NewReader(json))
+ ruserSearch := UserSearchFromJson(bytes.NewReader(json))
if userSearch.Term != ruserSearch.Term {
t.Fatal("Terms do not match")