APIv4 post /teams/search (#5931)

This commit is contained in:
Saturnino Abril
2017-04-04 02:34:14 +09:00
committed by Corey Hulen
parent 6b61834ab1
commit 43e795448f
10 changed files with 364 additions and 1 deletions

View File

@@ -20,6 +20,7 @@ func InitTeam() {
BaseRoutes.Teams.Handle("", ApiSessionRequired(createTeam)).Methods("POST") BaseRoutes.Teams.Handle("", ApiSessionRequired(createTeam)).Methods("POST")
BaseRoutes.Teams.Handle("", ApiSessionRequired(getAllTeams)).Methods("GET") BaseRoutes.Teams.Handle("", ApiSessionRequired(getAllTeams)).Methods("GET")
BaseRoutes.Teams.Handle("/search", ApiSessionRequired(searchTeams)).Methods("POST")
BaseRoutes.TeamsForUser.Handle("", ApiSessionRequired(getTeamsForUser)).Methods("GET") BaseRoutes.TeamsForUser.Handle("", ApiSessionRequired(getTeamsForUser)).Methods("GET")
BaseRoutes.TeamsForUser.Handle("/unread", ApiSessionRequired(getTeamsUnreadForUser)).Methods("GET") BaseRoutes.TeamsForUser.Handle("/unread", ApiSessionRequired(getTeamsUnreadForUser)).Methods("GET")
@@ -456,6 +457,35 @@ func getAllTeams(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(model.TeamListToJson(teams))) w.Write([]byte(model.TeamListToJson(teams)))
} }
func searchTeams(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.TeamSearchFromJson(r.Body)
if props == nil {
c.SetInvalidParam("team_search")
return
}
if len(props.Term) == 0 {
c.SetInvalidParam("term")
return
}
var teams []*model.Team
var err *model.AppError
if app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
teams, err = app.SearchAllTeams(props.Term)
} else {
teams, err = app.SearchOpenTeams(props.Term)
}
if err != nil {
c.Err = err
return
}
w.Write([]byte(model.TeamListToJson(teams)))
}
func teamExists(c *Context, w http.ResponseWriter, r *http.Request) { func teamExists(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireTeamName() c.RequireTeamName()
if c.Err != nil { if c.Err != nil {

View File

@@ -7,6 +7,7 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"net/http" "net/http"
"reflect"
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
@@ -405,6 +406,81 @@ func TestGetTeamByName(t *testing.T) {
CheckForbiddenStatus(t, resp) CheckForbiddenStatus(t, resp)
} }
func TestSearchAllTeams(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
defer TearDown()
Client := th.Client
oTeam := th.BasicTeam
pTeam := &model.Team{DisplayName: "PName", Name: GenerateTestTeamName(), Email: GenerateTestEmail(), Type: model.TEAM_INVITE}
Client.CreateTeam(pTeam)
rteams, resp := Client.SearchTeams(&model.TeamSearch{Term: oTeam.Name})
CheckNoError(t, resp)
if len(rteams) != 1 {
t.Fatal("should have returned 1 team")
}
if !reflect.DeepEqual(rteams[0], oTeam) {
t.Fatal("invalid team")
}
rteams, resp = Client.SearchTeams(&model.TeamSearch{Term: oTeam.DisplayName})
CheckNoError(t, resp)
if len(rteams) != 1 {
t.Fatal("should have returned 1 team")
}
if !reflect.DeepEqual(rteams[0], oTeam) {
t.Fatal("invalid team")
}
rteams, resp = Client.SearchTeams(&model.TeamSearch{Term: pTeam.Name})
CheckNoError(t, resp)
if len(rteams) != 0 {
t.Fatal("should have not returned team")
}
rteams, resp = Client.SearchTeams(&model.TeamSearch{Term: pTeam.DisplayName})
CheckNoError(t, resp)
if len(rteams) != 0 {
t.Fatal("should have not returned team")
}
rteams, resp = th.SystemAdminClient.SearchTeams(&model.TeamSearch{Term: oTeam.Name})
CheckNoError(t, resp)
if len(rteams) != 1 {
t.Fatal("should have returned 1 team")
}
rteams, resp = th.SystemAdminClient.SearchTeams(&model.TeamSearch{Term: pTeam.DisplayName})
CheckNoError(t, resp)
if len(rteams) != 1 {
t.Fatal("should have returned 1 team")
}
rteams, resp = Client.SearchTeams(&model.TeamSearch{Term: "junk"})
CheckNoError(t, resp)
if len(rteams) != 0 {
t.Fatal("should have not returned team")
}
Client.Logout()
rteams, resp = Client.SearchTeams(&model.TeamSearch{Term: pTeam.Name})
CheckUnauthorizedStatus(t, resp)
rteams, resp = Client.SearchTeams(&model.TeamSearch{Term: pTeam.DisplayName})
CheckUnauthorizedStatus(t, resp)
}
func TestGetTeamsForUser(t *testing.T) { func TestGetTeamsForUser(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin() th := Setup().InitBasic().InitSystemAdmin()
defer TearDown() defer TearDown()

View File

@@ -367,6 +367,22 @@ func GetAllOpenTeams() ([]*model.Team, *model.AppError) {
} }
} }
func SearchAllTeams(term string) ([]*model.Team, *model.AppError) {
if result := <-Srv.Store.Team().SearchAll(term); result.Err != nil {
return nil, result.Err
} else {
return result.Data.([]*model.Team), nil
}
}
func SearchOpenTeams(term string) ([]*model.Team, *model.AppError) {
if result := <-Srv.Store.Team().SearchOpen(term); result.Err != nil {
return nil, result.Err
} else {
return result.Data.([]*model.Team), nil
}
}
func GetAllOpenTeamsPage(offset int, limit int) ([]*model.Team, *model.AppError) { func GetAllOpenTeamsPage(offset int, limit int) ([]*model.Team, *model.AppError) {
if result := <-Srv.Store.Team().GetAllTeamPageListing(offset, limit); result.Err != nil { if result := <-Srv.Store.Team().GetAllTeamPageListing(offset, limit); result.Err != nil {
return nil, result.Err return nil, result.Err

View File

@@ -5511,6 +5511,14 @@
"id": "store.sql_team.save_member.save.app_error", "id": "store.sql_team.save_member.save.app_error",
"translation": "We couldn't save the team member" "translation": "We couldn't save the team member"
}, },
{
"id": "store.sql_team.search_open_team.app_error",
"translation": "We encountered an error searching open teams"
},
{
"id": "store.sql_team.search_all_team.app_error",
"translation": "We encountered an error searching teams"
},
{ {
"id": "store.sql_team.update.app_error", "id": "store.sql_team.update.app_error",
"translation": "We couldn't update the team" "translation": "We couldn't update the team"

View File

@@ -842,6 +842,16 @@ func (c *Client4) GetTeamByName(name, etag string) (*Team, *Response) {
} }
} }
// SearchTeams returns teams matching the provided search term.
func (c *Client4) SearchTeams(search *TeamSearch) ([]*Team, *Response) {
if r, err := c.DoApiPost(c.GetTeamsRoute()+"/search", search.ToJson()); err != nil {
return nil, &Response{StatusCode: r.StatusCode, Error: err}
} else {
defer closeBody(r)
return TeamListFromJson(r.Body), BuildResponse(r)
}
}
// TeamExists returns true or false if the team exist or not. // TeamExists returns true or false if the team exist or not.
func (c *Client4) TeamExists(name, etag string) (bool, *Response) { func (c *Client4) TeamExists(name, etag string) (bool, *Response) {
if r, err := c.DoApiGet(c.GetTeamByNameRoute(name)+"/exists", etag); err != nil { if r, err := c.DoApiGet(c.GetTeamByNameRoute(name)+"/exists", etag); err != nil {

35
model/team_search.go Normal file
View File

@@ -0,0 +1,35 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
type TeamSearch struct {
Term string `json:"term"`
}
// ToJson convert a TeamSearch to json string
func (c *TeamSearch) ToJson() string {
b, err := json.Marshal(c)
if err != nil {
return ""
}
return string(b)
}
// TeamSearchFromJson decodes the input and returns a TeamSearch
func TeamSearchFromJson(data io.Reader) *TeamSearch {
decoder := json.NewDecoder(data)
var cs TeamSearch
err := decoder.Decode(&cs)
if err == nil {
return &cs
}
return nil
}

19
model/team_search_test.go Normal file
View File

@@ -0,0 +1,19 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"strings"
"testing"
)
func TestTeamSearchJson(t *testing.T) {
teamSearch := TeamSearch{Term: NewId()}
json := teamSearch.ToJson()
rteamSearch := ChannelSearchFromJson(strings.NewReader(json))
if teamSearch.Term != rteamSearch.Term {
t.Fatal("Terms do not match")
}
}

View File

@@ -257,6 +257,48 @@ func (s SqlTeamStore) SearchByName(name string) StoreChannel {
return storeChannel return storeChannel
} }
func (s SqlTeamStore) SearchAll(term string) StoreChannel {
storeChannel := make(StoreChannel, 1)
go func() {
result := StoreResult{}
var teams []*model.Team
if _, err := s.GetReplica().Select(&teams, "SELECT * FROM Teams WHERE Name LIKE :Term OR DisplayName LIKE :Term", map[string]interface{}{"Term": term + "%"}); err != nil {
result.Err = model.NewLocAppError("SqlTeamStore.SearchAll", "store.sql_team.search_all_team.app_error", nil, "term="+term+", "+err.Error())
}
result.Data = teams
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (s SqlTeamStore) SearchOpen(term string) StoreChannel {
storeChannel := make(StoreChannel, 1)
go func() {
result := StoreResult{}
var teams []*model.Team
if _, err := s.GetReplica().Select(&teams, "SELECT * FROM Teams WHERE Type = 'O' AND (Name LIKE :Term OR DisplayName LIKE :Term)", map[string]interface{}{"Term": term + "%"}); err != nil {
result.Err = model.NewLocAppError("SqlTeamStore.SearchOpen", "store.sql_team.search_open_team.app_error", nil, "term="+term+", "+err.Error())
}
result.Data = teams
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (s SqlTeamStore) GetAll() StoreChannel { func (s SqlTeamStore) GetAll() StoreChannel {
storeChannel := make(StoreChannel, 1) storeChannel := make(StoreChannel, 1)

View File

@@ -4,9 +4,10 @@
package store package store
import ( import (
"github.com/mattermost/platform/model"
"testing" "testing"
"time" "time"
"github.com/mattermost/platform/model"
) )
func TestTeamStoreSave(t *testing.T) { func TestTeamStoreSave(t *testing.T) {
@@ -156,6 +157,130 @@ func TestTeamStoreSearchByName(t *testing.T) {
} }
} }
func TestTeamStoreSearchAll(t *testing.T) {
Setup()
o1 := model.Team{}
o1.DisplayName = "ADisplayName" + model.NewId()
o1.Name = "a" + model.NewId() + "a"
o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN
if err := (<-store.Team().Save(&o1)).Err; err != nil {
t.Fatal(err)
}
p2 := model.Team{}
p2.DisplayName = "BDisplayName" + model.NewId()
p2.Name = "b" + model.NewId() + "b"
p2.Email = model.NewId() + "@nowhere.com"
p2.Type = model.TEAM_INVITE
if err := (<-store.Team().Save(&p2)).Err; err != nil {
t.Fatal(err)
}
r1 := <-store.Team().SearchAll(o1.Name)
if r1.Err != nil {
t.Fatal(r1.Err)
}
if len(r1.Data.([]*model.Team)) != 1 {
t.Fatal("should have returned 1 team")
}
if r1.Data.([]*model.Team)[0].ToJson() != o1.ToJson() {
t.Fatal("invalid returned team")
}
r1 = <-store.Team().SearchAll(p2.DisplayName)
if r1.Err != nil {
t.Fatal(r1.Err)
}
if len(r1.Data.([]*model.Team)) != 1 {
t.Fatal("should have returned 1 team")
}
if r1.Data.([]*model.Team)[0].ToJson() != p2.ToJson() {
t.Fatal("invalid returned team")
}
r1 = <-store.Team().SearchAll("junk")
if r1.Err != nil {
t.Fatal(r1.Err)
}
if len(r1.Data.([]*model.Team)) != 0 {
t.Fatal("should have not returned a team")
}
}
func TestTeamStoreSearchOpen(t *testing.T) {
Setup()
o1 := model.Team{}
o1.DisplayName = "ADisplayName" + model.NewId()
o1.Name = "a" + model.NewId() + "a"
o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN
if err := (<-store.Team().Save(&o1)).Err; err != nil {
t.Fatal(err)
}
p2 := model.Team{}
p2.DisplayName = "BDisplayName" + model.NewId()
p2.Name = "b" + model.NewId() + "b"
p2.Email = model.NewId() + "@nowhere.com"
p2.Type = model.TEAM_INVITE
if err := (<-store.Team().Save(&p2)).Err; err != nil {
t.Fatal(err)
}
r1 := <-store.Team().SearchOpen(o1.Name)
if r1.Err != nil {
t.Fatal(r1.Err)
}
if len(r1.Data.([]*model.Team)) != 1 {
t.Fatal("should have returned 1 team")
}
if r1.Data.([]*model.Team)[0].ToJson() != o1.ToJson() {
t.Fatal("invalid returned team")
}
r1 = <-store.Team().SearchOpen(o1.DisplayName)
if r1.Err != nil {
t.Fatal(r1.Err)
}
if len(r1.Data.([]*model.Team)) != 1 {
t.Fatal("should have returned 1 team")
}
if r1.Data.([]*model.Team)[0].ToJson() != o1.ToJson() {
t.Fatal("invalid returned team")
}
r1 = <-store.Team().SearchOpen(p2.Name)
if r1.Err != nil {
t.Fatal(r1.Err)
}
if len(r1.Data.([]*model.Team)) != 0 {
t.Fatal("should have not returned a team")
}
r1 = <-store.Team().SearchOpen(p2.DisplayName)
if r1.Err != nil {
t.Fatal(r1.Err)
}
if len(r1.Data.([]*model.Team)) != 0 {
t.Fatal("should have not returned a team")
}
r1 = <-store.Team().SearchOpen("junk")
if r1.Err != nil {
t.Fatal(r1.Err)
}
if len(r1.Data.([]*model.Team)) != 0 {
t.Fatal("should have not returned a team")
}
}
func TestTeamStoreGetByIniviteId(t *testing.T) { func TestTeamStoreGetByIniviteId(t *testing.T) {
Setup() Setup()

View File

@@ -61,6 +61,8 @@ type TeamStore interface {
Get(id string) StoreChannel Get(id string) StoreChannel
GetByName(name string) StoreChannel GetByName(name string) StoreChannel
SearchByName(name string) StoreChannel SearchByName(name string) StoreChannel
SearchAll(term string) StoreChannel
SearchOpen(term string) StoreChannel
GetAll() StoreChannel GetAll() StoreChannel
GetAllPage(offset int, limit int) StoreChannel GetAllPage(offset int, limit int) StoreChannel
GetAllTeamListing() StoreChannel GetAllTeamListing() StoreChannel