From 02d581c1599c5d50cc507bd2633f1e3c34b1cc84 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Tue, 29 Nov 2016 10:12:59 -0500 Subject: PLT-4697 Update channel switcher to autocomplete all users on the system (#4624) * Add autocomplete API for system-wide users * Update channel switcher to autocomplete all users on the system --- api/user.go | 22 +++++++++++++ api/user_test.go | 38 ++++++++++++++++++++++ model/client.go | 13 ++++++++ webapp/actions/user_actions.jsx | 18 ++++++++++ webapp/client/client.jsx | 11 ++++++- .../suggestion/switch_channel_provider.jsx | 7 ++-- webapp/tests/client_user.test.jsx | 15 +++++++++ 7 files changed, 119 insertions(+), 5 deletions(-) diff --git a/api/user.go b/api/user.go index 26066dabc..f5f2582b3 100644 --- a/api/user.go +++ b/api/user.go @@ -59,6 +59,7 @@ func InitUser() { BaseRoutes.NeedChannel.Handle("/users/not_in_channel/{offset:[0-9]+}/{limit:[0-9]+}", ApiUserRequired(getProfilesNotInChannel)).Methods("GET") BaseRoutes.Users.Handle("/search", ApiUserRequired(searchUsers)).Methods("POST") BaseRoutes.Users.Handle("/ids", ApiUserRequired(getProfilesByIds)).Methods("POST") + BaseRoutes.Users.Handle("/autocomplete", ApiUserRequired(autocompleteUsers)).Methods("GET") BaseRoutes.NeedTeam.Handle("/users/autocomplete", ApiUserRequired(autocompleteUsersInTeam)).Methods("GET") BaseRoutes.NeedChannel.Handle("/users/autocomplete", ApiUserRequired(autocompleteUsersInChannel)).Methods("GET") @@ -2780,3 +2781,24 @@ func autocompleteUsersInTeam(c *Context, w http.ResponseWriter, r *http.Request) w.Write([]byte(autocomplete.ToJson())) } + +func autocompleteUsers(c *Context, w http.ResponseWriter, r *http.Request) { + term := r.URL.Query().Get("term") + + uchan := Srv.Store.User().Search("", term, map[string]bool{}) + + var profiles []*model.User + + if result := <-uchan; result.Err != nil { + c.Err = result.Err + return + } else { + profiles = result.Data.([]*model.User) + + for _, p := range profiles { + sanitizeProfile(c, p) + } + } + + w.Write([]byte(model.UserListToJson(profiles))) +} diff --git a/api/user_test.go b/api/user_test.go index a10cee961..02ea71c83 100644 --- a/api/user_test.go +++ b/api/user_test.go @@ -2275,6 +2275,44 @@ func TestAutocompleteUsers(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient + if result, err := Client.AutocompleteUsers(th.BasicUser.Username); err != nil { + t.Fatal(err) + } else { + users := result.Data.([]*model.User) + if len(users) != 1 { + t.Fatal("should have returned 1 user in") + } + } + + if result, err := Client.AutocompleteUsers(""); err != nil { + t.Fatal(err) + } else { + users := result.Data.([]*model.User) + if len(users) == 0 { + t.Fatal("should have many users") + } + } + + notInTeamUser := th.CreateUser(Client) + + if result, err := Client.AutocompleteUsers(notInTeamUser.Username); err != nil { + t.Fatal(err) + } else { + users := result.Data.([]*model.User) + if len(users) != 1 { + t.Fatal("should have returned 1 user in") + } + } + + if result, err := Client.AutocompleteUsersInTeam(notInTeamUser.Username); err != nil { + t.Fatal(err) + } else { + autocomplete := result.Data.(*model.UserAutocompleteInTeam) + if len(autocomplete.InTeam) != 0 { + t.Fatal("should have returned 0 users") + } + } + if result, err := Client.AutocompleteUsersInTeam(th.BasicUser.Username); err != nil { t.Fatal(err) } else { diff --git a/model/client.go b/model/client.go index 30eba99f8..631de9c56 100644 --- a/model/client.go +++ b/model/client.go @@ -621,6 +621,19 @@ func (c *Client) AutocompleteUsersInTeam(term string) (*Result, *AppError) { } } +// AutocompleteUsers returns a list for autocompletion of users on the system that match the provided term, +// matching against username, full name and nickname. Must be authenticated. +func (c *Client) AutocompleteUsers(term string) (*Result, *AppError) { + url := fmt.Sprintf("/users/autocomplete?term=%s", url.QueryEscape(term)) + if r, err := c.DoApiGet(url, "", ""); err != nil { + return nil, err + } else { + defer closeBody(r) + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), UserListFromJson(r.Body)}, nil + } +} + // LoginById authenticates a user by user id and password. func (c *Client) LoginById(id string, password string) (*Result, *AppError) { m := make(map[string]string) diff --git a/webapp/actions/user_actions.jsx b/webapp/actions/user_actions.jsx index ce2246d30..455ca1730 100644 --- a/webapp/actions/user_actions.jsx +++ b/webapp/actions/user_actions.jsx @@ -318,6 +318,24 @@ export function autocompleteUsersInTeam(username, success, error) { ); } +export function autocompleteUsers(username, success, error) { + Client.autocompleteUsers( + username, + (data) => { + if (success) { + success(data); + } + }, + (err) => { + AsyncClient.dispatchError(err, 'autocompleteUsers'); + + if (error) { + error(err); + } + } + ); +} + export function updateUser(username, success, error) { Client.updateUser( username, diff --git a/webapp/client/client.jsx b/webapp/client/client.jsx index 38cc2f111..ff42fb7ac 100644 --- a/webapp/client/client.jsx +++ b/webapp/client/client.jsx @@ -1112,7 +1112,7 @@ export default class Client { set(this.defaultHeaders). type('application/json'). accept('application/json'). - end(this.handleResponse.bind(this, 'autocompleteUsers', success, error)); + end(this.handleResponse.bind(this, 'autocompleteUsersInChannel', success, error)); } autocompleteUsersInTeam(term, success, error) { @@ -1121,6 +1121,15 @@ export default class Client { set(this.defaultHeaders). type('application/json'). accept('application/json'). + end(this.handleResponse.bind(this, 'autocompleteUsersInTeam', success, error)); + } + + autocompleteUsers(term, success, error) { + request. + get(`${this.getUsersRoute()}/autocomplete?term=${encodeURIComponent(term)}`). + set(this.defaultHeaders). + type('application/json'). + accept('application/json'). end(this.handleResponse.bind(this, 'autocompleteUsers', success, error)); } diff --git a/webapp/components/suggestion/switch_channel_provider.jsx b/webapp/components/suggestion/switch_channel_provider.jsx index 8178722ef..534a7e30f 100644 --- a/webapp/components/suggestion/switch_channel_provider.jsx +++ b/webapp/components/suggestion/switch_channel_provider.jsx @@ -6,7 +6,7 @@ import Suggestion from './suggestion.jsx'; import ChannelStore from 'stores/channel_store.jsx'; import UserStore from 'stores/user_store.jsx'; -import {autocompleteUsersInTeam} from 'actions/user_actions.jsx'; +import {autocompleteUsers} from 'actions/user_actions.jsx'; import AppDispatcher from 'dispatcher/app_dispatcher.jsx'; import {Constants, ActionTypes} from 'utils/constants.jsx'; @@ -64,10 +64,9 @@ export default class SwitchChannelProvider { const channels = []; function autocomplete() { - autocompleteUsersInTeam( + autocompleteUsers( channelPrefix, - (data) => { - const users = data.in_team; + (users) => { const currentId = UserStore.getCurrentId(); for (const id of Object.keys(allChannels)) { diff --git a/webapp/tests/client_user.test.jsx b/webapp/tests/client_user.test.jsx index 2e5b80dd7..5e5eb6821 100644 --- a/webapp/tests/client_user.test.jsx +++ b/webapp/tests/client_user.test.jsx @@ -602,6 +602,21 @@ describe('Client.User', function() { }); }); + it('autocompleteUsers', function(done) { + TestHelper.initBasic(() => { + TestHelper.basicClient().autocompleteUsers( + 'uid', + function(data) { + assert.equal(data != null, true); + done(); + }, + function(err) { + done(new Error(err.message)); + } + ); + }); + }); + it('getStatusesByIds', function(done) { TestHelper.initBasic(() => { var ids = []; -- cgit v1.2.3-1-g7c22