Implement GET /users/username/{username} endpoint for APIv4 (#5310)

* added get user by username endpoint

* added get user by username unit test and driver

* changed username length to 22 characters max

* changed Params to UserName to Username

* reorganized get user by username and get user by email formatting in model/client4
This commit is contained in:
Ruzette Tanyag
2017-02-08 05:00:16 -05:00
committed by Joram Wilander
parent 5462f0119e
commit 0162d8ad08
8 changed files with 133 additions and 3 deletions

View File

@@ -90,7 +90,7 @@ func InitApi(full bool) {
BaseRoutes.Users = BaseRoutes.ApiRoot.PathPrefix("/users").Subrouter()
BaseRoutes.User = BaseRoutes.Users.PathPrefix("/{user_id:[A-Za-z0-9]+}").Subrouter()
BaseRoutes.UserByUsername = BaseRoutes.Users.PathPrefix("/username/{username:[A-Za-z0-9_-.]+}").Subrouter()
BaseRoutes.UserByUsername = BaseRoutes.Users.PathPrefix("/username/{username:[A-Za-z0-9\\_\\-\\.]+}").Subrouter()
BaseRoutes.UserByEmail = BaseRoutes.Users.PathPrefix("/email/{email}").Subrouter()
BaseRoutes.Teams = BaseRoutes.ApiRoot.PathPrefix("/teams").Subrouter()

View File

@@ -247,7 +247,7 @@ func GenerateTestEmail() string {
}
func GenerateTestUsername() string {
return "fakeuser" + model.NewId()
return "fakeuser" + model.NewRandomString(13)
}
func GenerateTestTeamName() string {

View File

@@ -359,6 +359,22 @@ func (c *Context) RequireChannelId() *Context {
return c
}
func (c *Context) RequireUsername() *Context {
if c.Err != nil {
return c
}
if len(c.Params.Username) < 3 {
c.SetInvalidUrlParam("username")
}
if len(c.Params.Username) > 22 {
c.SetInvalidUrlParam("username")
}
return c
}
func (c *Context) RequireEmail() *Context {
if c.Err != nil {
return c
@@ -371,3 +387,4 @@ func (c *Context) RequireEmail() *Context {
return c
}

View File

@@ -26,6 +26,7 @@ type ApiParams struct {
HookId string
EmojiId string
Email string
Username string
Page int
PerPage int
}
@@ -71,6 +72,10 @@ func ApiParamsFromRequest(r *http.Request) *ApiParams {
params.Email = val
}
if val, ok := props["username"]; ok {
params.Username = val
}
if val, err := strconv.Atoi(r.URL.Query().Get("page")); err != nil {
params.Page = PAGE_DEFAULT
} else {

View File

@@ -30,6 +30,7 @@ func InitUser() {
BaseRoutes.Users.Handle("/login", ApiHandler(login)).Methods("POST")
BaseRoutes.Users.Handle("/logout", ApiHandler(logout)).Methods("POST")
BaseRoutes.UserByUsername.Handle("", ApiSessionRequired(getUserByUsername)).Methods("GET")
BaseRoutes.UserByEmail.Handle("", ApiSessionRequired(getUserByEmail)).Methods("GET")
}
@@ -93,6 +94,34 @@ func getUser(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
func getUserByUsername(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireUsername()
if c.Err != nil {
return
}
// No permission check required
var user *model.User
var err *model.AppError
if user, err = app.GetUserByUsername(c.Params.Username); err != nil {
c.Err = err
return
}
etag := user.Etag(utils.Cfg.PrivacySettings.ShowFullName, utils.Cfg.PrivacySettings.ShowEmailAddress)
if HandleEtag(etag, "Get User", w, r) {
return
} else {
app.SanitizeProfile(user, c.IsSystemAdmin())
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
w.Write([]byte(user.ToJson()))
return
}
}
func getUserByEmail(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireEmail()
if c.Err != nil {

View File

@@ -133,6 +133,70 @@ func TestGetUser(t *testing.T) {
}
}
func TestGetUserByUsername(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
defer TearDown()
Client := th.Client
user := th.BasicUser
ruser, resp := Client.GetUserByUsername(user.Username, "")
CheckNoError(t, resp)
CheckUserSanitization(t, ruser)
if ruser.Email != user.Email {
t.Fatal("emails did not match")
}
ruser, resp = Client.GetUserByUsername(user.Username, resp.Etag)
CheckEtag(t, ruser, resp)
_, resp = Client.GetUserByUsername(GenerateTestUsername(), "")
CheckNotFoundStatus(t, resp)
_, resp = Client.GetUserByUsername(model.NewRandomString(25), "")
CheckBadRequestStatus(t, resp)
// Check against privacy config settings
emailPrivacy := utils.Cfg.PrivacySettings.ShowEmailAddress
namePrivacy := utils.Cfg.PrivacySettings.ShowFullName
defer func() {
utils.Cfg.PrivacySettings.ShowEmailAddress = emailPrivacy
utils.Cfg.PrivacySettings.ShowFullName = namePrivacy
}()
utils.Cfg.PrivacySettings.ShowEmailAddress = false
utils.Cfg.PrivacySettings.ShowFullName = false
ruser, resp = Client.GetUserByUsername(user.Username, "")
CheckNoError(t, resp)
if ruser.Email != "" {
t.Fatal("email should be blank")
}
if ruser.FirstName != "" {
t.Fatal("first name should be blank")
}
if ruser.LastName != "" {
t.Fatal("last name should be blank")
}
Client.Logout()
_, resp = Client.GetUserByUsername(user.Username, "")
CheckUnauthorizedStatus(t, resp)
// System admins should ignore privacy settings
ruser, resp = th.SystemAdminClient.GetUserByUsername(user.Username, resp.Etag)
if ruser.Email == "" {
t.Fatal("email should not be blank")
}
if ruser.FirstName == "" {
t.Fatal("first name should not be blank")
}
if ruser.LastName == "" {
t.Fatal("last name should not be blank")
}
}
func TestGetUserByEmail(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
defer TearDown()

View File

@@ -323,7 +323,8 @@ func GetUser(userId string) (*model.User, *model.AppError) {
}
func GetUserByUsername(username string) (*model.User, *model.AppError) {
if result := <-Srv.Store.User().GetByUsername(username); result.Err != nil {
if result := <-Srv.Store.User().GetByUsername(username); result.Err != nil && result.Err.Id == "store.sql_user.get_by_username.app_error" {
result.Err.StatusCode = http.StatusNotFound
return nil, result.Err
} else {
return result.Data.(*model.User), nil

View File

@@ -56,6 +56,10 @@ func (c *Client4) GetUserRoute(userId string) string {
return fmt.Sprintf(c.GetUsersRoute()+"/%v", userId)
}
func (c *Client4) GetUserByUsernameRoute(userName string) string {
return fmt.Sprintf(c.GetUsersRoute()+"/username/%v", userName)
}
func (c *Client4) GetUserByEmailRoute(email string) string {
return fmt.Sprintf(c.GetUsersRoute()+"/email/%v", email)
}
@@ -226,6 +230,16 @@ func (c *Client4) GetUser(userId, etag string) (*User, *Response) {
}
}
// GetUserByUsername returns a user based on the provided user name string.
func (c *Client4) GetUserByUsername(userName, etag string) (*User, *Response) {
if r, err := c.DoApiGet(c.GetUserByUsernameRoute(userName), etag); err != nil {
return nil, &Response{StatusCode: r.StatusCode, Error: err}
} else {
defer closeBody(r)
return UserFromJson(r.Body), BuildResponse(r)
}
}
// GetUserByEmail returns a user based on the provided user email string.
func (c *Client4) GetUserByEmail(email, etag string) (*User, *Response) {
if r, err := c.DoApiGet(c.GetUserByEmailRoute(email), etag); err != nil {