Implement a few channel member endpoints for APIv4 (#5304)

* Implement GET /channels/{channel_id}/members

* Implement GET /channels/{channel_id}/members/{user_id} endpoint for APIv4

* Implement /users/{user_id}/teams/{team_id}/channels/members endpoint for APIv4

* Fix unit test
Этот коммит содержится в:
Joram Wilander
2017-02-07 14:58:27 -08:00
коммит произвёл GitHub
родитель 487bb56a9b
Коммит 5462f0119e
8 изменённых файлов: 274 добавлений и 15 удалений

Просмотреть файл

@@ -38,7 +38,7 @@ type Routes struct {
ChannelsForTeam *mux.Router // 'api/v4/teams/{team_id:[A-Za-z0-9]+}/channels' ChannelsForTeam *mux.Router // 'api/v4/teams/{team_id:[A-Za-z0-9]+}/channels'
ChannelMembers *mux.Router // 'api/v4/channels/{channel_id:[A-Za-z0-9]+}/members' ChannelMembers *mux.Router // 'api/v4/channels/{channel_id:[A-Za-z0-9]+}/members'
ChannelMember *mux.Router // 'api/v4/channels/{channel_id:[A-Za-z0-9]+}/members/{user_id:[A-Za-z0-9]+}' ChannelMember *mux.Router // 'api/v4/channels/{channel_id:[A-Za-z0-9]+}/members/{user_id:[A-Za-z0-9]+}'
ChannelMembersForUser *mux.Router // 'api/v4/users/{user_id:[A-Za-z0-9]+}/channels/members' ChannelMembersForUser *mux.Router // 'api/v4/users/{user_id:[A-Za-z0-9]+}/teams/{team_id:[A-Za-z0-9]+}/channels/members'
Posts *mux.Router // 'api/v4/posts' Posts *mux.Router // 'api/v4/posts'
Post *mux.Router // 'api/v4/posts/{post_id:[A-Za-z0-9]+}' Post *mux.Router // 'api/v4/posts/{post_id:[A-Za-z0-9]+}'
@@ -106,7 +106,7 @@ func InitApi(full bool) {
BaseRoutes.ChannelsForTeam = BaseRoutes.Team.PathPrefix("/channels").Subrouter() BaseRoutes.ChannelsForTeam = BaseRoutes.Team.PathPrefix("/channels").Subrouter()
BaseRoutes.ChannelMembers = BaseRoutes.Channel.PathPrefix("/members").Subrouter() BaseRoutes.ChannelMembers = BaseRoutes.Channel.PathPrefix("/members").Subrouter()
BaseRoutes.ChannelMember = BaseRoutes.ChannelMembers.PathPrefix("/{user_id:[A-Za-z0-9]+}").Subrouter() BaseRoutes.ChannelMember = BaseRoutes.ChannelMembers.PathPrefix("/{user_id:[A-Za-z0-9]+}").Subrouter()
BaseRoutes.ChannelMembersForUser = BaseRoutes.User.PathPrefix("/channels/members").Subrouter() BaseRoutes.ChannelMembersForUser = BaseRoutes.User.PathPrefix("/teams/{team_id:[A-Za-z0-9]+}/channels/members").Subrouter()
BaseRoutes.Posts = BaseRoutes.ApiRoot.PathPrefix("/posts").Subrouter() BaseRoutes.Posts = BaseRoutes.ApiRoot.PathPrefix("/posts").Subrouter()
BaseRoutes.Post = BaseRoutes.Posts.PathPrefix("/{post_id:[A-Za-z0-9]+}").Subrouter() BaseRoutes.Post = BaseRoutes.Posts.PathPrefix("/{post_id:[A-Za-z0-9]+}").Subrouter()

Просмотреть файл

@@ -17,6 +17,10 @@ func InitChannel() {
BaseRoutes.Channels.Handle("", ApiSessionRequired(createChannel)).Methods("POST") BaseRoutes.Channels.Handle("", ApiSessionRequired(createChannel)).Methods("POST")
BaseRoutes.Channels.Handle("/direct", ApiSessionRequired(createDirectChannel)).Methods("POST") BaseRoutes.Channels.Handle("/direct", ApiSessionRequired(createDirectChannel)).Methods("POST")
BaseRoutes.ChannelMembers.Handle("", ApiSessionRequired(getChannelMembers)).Methods("GET")
BaseRoutes.ChannelMembersForUser.Handle("", ApiSessionRequired(getChannelMembersForUser)).Methods("GET")
BaseRoutes.ChannelMember.Handle("", ApiSessionRequired(getChannelMember)).Methods("GET")
} }
func createChannel(c *Context, w http.ResponseWriter, r *http.Request) { func createChannel(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -83,3 +87,65 @@ func createDirectChannel(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(sc.ToJson())) w.Write([]byte(sc.ToJson()))
} }
} }
func getChannelMembers(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireChannelId()
if c.Err != nil {
return
}
if !app.SessionHasPermissionToChannel(c.Session, c.Params.ChannelId, model.PERMISSION_READ_CHANNEL) {
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
return
}
if members, err := app.GetChannelMembersPage(c.Params.ChannelId, c.Params.Page, c.Params.PerPage); err != nil {
c.Err = err
return
} else {
w.Write([]byte(members.ToJson()))
}
}
func getChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireChannelId().RequireUserId()
if c.Err != nil {
return
}
if !app.SessionHasPermissionToChannel(c.Session, c.Params.ChannelId, model.PERMISSION_READ_CHANNEL) {
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
return
}
if member, err := app.GetChannelMember(c.Params.ChannelId, c.Params.UserId); err != nil {
c.Err = err
return
} else {
w.Write([]byte(member.ToJson()))
}
}
func getChannelMembersForUser(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireUserId().RequireTeamId()
if c.Err != nil {
return
}
if !app.SessionHasPermissionToTeam(c.Session, c.Params.TeamId, model.PERMISSION_VIEW_TEAM) {
c.SetPermissionError(model.PERMISSION_VIEW_TEAM)
return
}
if c.Session.UserId != c.Params.UserId && !app.SessionHasPermissionToTeam(c.Session, c.Params.TeamId, model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
}
if members, err := app.GetChannelMembersForUser(c.Params.TeamId, c.Params.UserId); err != nil {
c.Err = err
return
} else {
w.Write([]byte(members.ToJson()))
}
}

Просмотреть файл

@@ -14,6 +14,7 @@ import (
func TestCreateChannel(t *testing.T) { func TestCreateChannel(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin() th := Setup().InitBasic().InitSystemAdmin()
defer TearDown()
Client := th.Client Client := th.Client
team := th.BasicTeam team := th.BasicTeam
@@ -219,3 +220,148 @@ func TestCreateDirectChannel(t *testing.T) {
_, resp = th.SystemAdminClient.CreateDirectChannel(user3.Id, user2.Id) _, resp = th.SystemAdminClient.CreateDirectChannel(user3.Id, user2.Id)
CheckNoError(t, resp) CheckNoError(t, resp)
} }
func TestGetChannelMembers(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
defer TearDown()
Client := th.Client
members, resp := Client.GetChannelMembers(th.BasicChannel.Id, 0, 60, "")
CheckNoError(t, resp)
if len(*members) != 3 {
t.Fatal("should only be 3 users in channel")
}
members, resp = Client.GetChannelMembers(th.BasicChannel.Id, 0, 2, "")
CheckNoError(t, resp)
if len(*members) != 2 {
t.Fatal("should only be 2 users")
}
members, resp = Client.GetChannelMembers(th.BasicChannel.Id, 1, 1, "")
CheckNoError(t, resp)
if len(*members) != 1 {
t.Fatal("should only be 1 user")
}
members, resp = Client.GetChannelMembers(th.BasicChannel.Id, 1000, 100000, "")
CheckNoError(t, resp)
if len(*members) != 0 {
t.Fatal("should be 0 users")
}
_, resp = Client.GetChannelMembers("", 0, 60, "")
CheckNotFoundStatus(t, resp)
_, resp = Client.GetChannelMembers("junk", 0, 60, "")
CheckBadRequestStatus(t, resp)
_, resp = Client.GetChannelMembers(model.NewId(), 0, 60, "")
CheckForbiddenStatus(t, resp)
Client.Logout()
_, resp = Client.GetChannelMembers(th.BasicChannel.Id, 0, 60, "")
CheckUnauthorizedStatus(t, resp)
user := th.CreateUser()
Client.Login(user.Email, user.Password)
_, resp = Client.GetChannelMembers(th.BasicChannel.Id, 0, 60, "")
CheckForbiddenStatus(t, resp)
_, resp = th.SystemAdminClient.GetChannelMembers(th.BasicChannel.Id, 0, 60, "")
CheckNoError(t, resp)
}
func TestGetChannelMember(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
defer TearDown()
Client := th.Client
member, resp := Client.GetChannelMember(th.BasicChannel.Id, th.BasicUser.Id, "")
CheckNoError(t, resp)
if member.ChannelId != th.BasicChannel.Id {
t.Fatal("wrong channel id")
}
if member.UserId != th.BasicUser.Id {
t.Fatal("wrong user id")
}
_, resp = Client.GetChannelMember("", th.BasicUser.Id, "")
CheckNotFoundStatus(t, resp)
_, resp = Client.GetChannelMember("junk", th.BasicUser.Id, "")
CheckBadRequestStatus(t, resp)
_, resp = Client.GetChannelMember(model.NewId(), th.BasicUser.Id, "")
CheckForbiddenStatus(t, resp)
_, resp = Client.GetChannelMember(th.BasicChannel.Id, "", "")
CheckNotFoundStatus(t, resp)
_, resp = Client.GetChannelMember(th.BasicChannel.Id, "junk", "")
CheckBadRequestStatus(t, resp)
_, resp = Client.GetChannelMember(th.BasicChannel.Id, model.NewId(), "")
CheckNotFoundStatus(t, resp)
Client.Logout()
_, resp = Client.GetChannelMember(th.BasicChannel.Id, th.BasicUser.Id, "")
CheckUnauthorizedStatus(t, resp)
user := th.CreateUser()
Client.Login(user.Email, user.Password)
_, resp = Client.GetChannelMember(th.BasicChannel.Id, th.BasicUser.Id, "")
CheckForbiddenStatus(t, resp)
_, resp = th.SystemAdminClient.GetChannelMember(th.BasicChannel.Id, th.BasicUser.Id, "")
CheckNoError(t, resp)
}
func TestGetChannelMembersForUser(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
defer TearDown()
Client := th.Client
members, resp := Client.GetChannelMembersForUser(th.BasicUser.Id, th.BasicTeam.Id, "")
CheckNoError(t, resp)
if len(*members) != 3 {
t.Fatal("should have 3 members on team")
}
_, resp = Client.GetChannelMembersForUser("", th.BasicTeam.Id, "")
CheckNotFoundStatus(t, resp)
_, resp = Client.GetChannelMembersForUser("junk", th.BasicTeam.Id, "")
CheckBadRequestStatus(t, resp)
_, resp = Client.GetChannelMembersForUser(model.NewId(), th.BasicTeam.Id, "")
CheckForbiddenStatus(t, resp)
_, resp = Client.GetChannelMembersForUser(th.BasicUser.Id, "", "")
CheckNotFoundStatus(t, resp)
_, resp = Client.GetChannelMembersForUser(th.BasicUser.Id, "junk", "")
CheckBadRequestStatus(t, resp)
_, resp = Client.GetChannelMembersForUser(th.BasicUser.Id, model.NewId(), "")
CheckForbiddenStatus(t, resp)
Client.Logout()
_, resp = Client.GetChannelMembersForUser(th.BasicUser.Id, th.BasicTeam.Id, "")
CheckUnauthorizedStatus(t, resp)
user := th.CreateUser()
Client.Login(user.Email, user.Password)
_, resp = Client.GetChannelMembersForUser(th.BasicUser.Id, th.BasicTeam.Id, "")
CheckForbiddenStatus(t, resp)
_, resp = th.SystemAdminClient.GetChannelMembersForUser(th.BasicUser.Id, th.BasicTeam.Id, "")
CheckNoError(t, resp)
}

Просмотреть файл

@@ -15,11 +15,11 @@ import (
) )
func MakeDirectChannelVisible(channelId string) *model.AppError { func MakeDirectChannelVisible(channelId string) *model.AppError {
var members []model.ChannelMember var members model.ChannelMembers
if result := <-Srv.Store.Channel().GetMembers(channelId); result.Err != nil { if result := <-Srv.Store.Channel().GetMembers(channelId, 0, 100); result.Err != nil {
return result.Err return result.Err
} else { } else {
members = result.Data.([]model.ChannelMember) members = *(result.Data.(*model.ChannelMembers))
} }
if len(members) != 2 { if len(members) != 2 {
@@ -582,6 +582,14 @@ func GetChannelMember(channelId string, userId string) (*model.ChannelMember, *m
} }
} }
func GetChannelMembersPage(channelId string, page, perPage int) (*model.ChannelMembers, *model.AppError) {
if result := <-Srv.Store.Channel().GetMembers(channelId, page*perPage, perPage); result.Err != nil {
return nil, result.Err
} else {
return result.Data.(*model.ChannelMembers), nil
}
}
func GetChannelMembersByIds(channelId string, userIds []string) (*model.ChannelMembers, *model.AppError) { func GetChannelMembersByIds(channelId string, userIds []string) (*model.ChannelMembers, *model.AppError) {
if result := <-Srv.Store.Channel().GetMembersByIds(channelId, userIds); result.Err != nil { if result := <-Srv.Store.Channel().GetMembersByIds(channelId, userIds); result.Err != nil {
return nil, result.Err return nil, result.Err

Просмотреть файл

@@ -80,6 +80,14 @@ func (c *Client4) GetChannelRoute(channelId string) string {
return fmt.Sprintf(c.GetChannelsRoute()+"/%v", channelId) return fmt.Sprintf(c.GetChannelsRoute()+"/%v", channelId)
} }
func (c *Client4) GetChannelMembersRoute(channelId string) string {
return fmt.Sprintf(c.GetChannelRoute(channelId) + "/members")
}
func (c *Client4) GetChannelMemberRoute(channelId, userId string) string {
return fmt.Sprintf(c.GetChannelMembersRoute(channelId)+"/%v", userId)
}
func (c *Client4) DoApiGet(url string, etag string) (*http.Response, *AppError) { func (c *Client4) DoApiGet(url string, etag string) (*http.Response, *AppError) {
return c.DoApiRequest(http.MethodGet, url, "", etag) return c.DoApiRequest(http.MethodGet, url, "", etag)
} }
@@ -414,5 +422,36 @@ func (c *Client4) CreateDirectChannel(userId1, userId2 string) (*Channel, *Respo
} }
} }
// GetChannelMembers gets a page of channel members.
func (c *Client4) GetChannelMembers(channelId string, page, perPage int, etag string) (*ChannelMembers, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
if r, err := c.DoApiGet(c.GetChannelMembersRoute(channelId)+query, etag); err != nil {
return nil, &Response{StatusCode: r.StatusCode, Error: err}
} else {
defer closeBody(r)
return ChannelMembersFromJson(r.Body), BuildResponse(r)
}
}
// GetChannelMember gets a channel member.
func (c *Client4) GetChannelMember(channelId, userId, etag string) (*ChannelMember, *Response) {
if r, err := c.DoApiGet(c.GetChannelMemberRoute(channelId, userId), etag); err != nil {
return nil, &Response{StatusCode: r.StatusCode, Error: err}
} else {
defer closeBody(r)
return ChannelMemberFromJson(r.Body), BuildResponse(r)
}
}
// GetChannelMembersForUser gets all the channel members for a user on a team.
func (c *Client4) GetChannelMembersForUser(userId, teamId, etag string) (*ChannelMembers, *Response) {
if r, err := c.DoApiGet(fmt.Sprintf(c.GetUserRoute(userId)+"/teams/%v/channels/members", teamId), etag); err != nil {
return nil, &Response{StatusCode: r.StatusCode, Error: err}
} else {
defer closeBody(r)
return ChannelMembersFromJson(r.Body), BuildResponse(r)
}
}
// Post Section // Post Section
// to be filled in.. // to be filled in..

Просмотреть файл

@@ -751,18 +751,18 @@ func (s SqlChannelStore) UpdateMember(member *model.ChannelMember) StoreChannel
return storeChannel return storeChannel
} }
func (s SqlChannelStore) GetMembers(channelId string) StoreChannel { func (s SqlChannelStore) GetMembers(channelId string, offset, limit int) StoreChannel {
storeChannel := make(StoreChannel, 1) storeChannel := make(StoreChannel, 1)
go func() { go func() {
result := StoreResult{} result := StoreResult{}
var members []model.ChannelMember var members model.ChannelMembers
_, err := s.GetReplica().Select(&members, "SELECT * FROM ChannelMembers WHERE ChannelId = :ChannelId", map[string]interface{}{"ChannelId": channelId}) _, err := s.GetReplica().Select(&members, "SELECT * FROM ChannelMembers WHERE ChannelId = :ChannelId LIMIT :Limit OFFSET :Offset", map[string]interface{}{"ChannelId": channelId, "Limit": limit, "Offset": offset})
if err != nil { if err != nil {
result.Err = model.NewLocAppError("SqlChannelStore.GetMembers", "store.sql_channel.get_members.app_error", nil, "channel_id="+channelId+err.Error()) result.Err = model.NewLocAppError("SqlChannelStore.GetMembers", "store.sql_channel.get_members.app_error", nil, "channel_id="+channelId+err.Error())
} else { } else {
result.Data = members result.Data = &members
} }
storeChannel <- result storeChannel <- result
@@ -782,7 +782,7 @@ func (s SqlChannelStore) GetMember(channelId string, userId string) StoreChannel
if err := s.GetReplica().SelectOne(&member, "SELECT * FROM ChannelMembers WHERE ChannelId = :ChannelId AND UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId}); err != nil { if err := s.GetReplica().SelectOne(&member, "SELECT * FROM ChannelMembers WHERE ChannelId = :ChannelId AND UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId}); err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
result.Err = model.NewLocAppError("SqlChannelStore.GetMember", MISSING_CHANNEL_MEMBER_ERROR, nil, "channel_id="+channelId+"user_id="+userId+","+err.Error()) result.Err = model.NewAppError("SqlChannelStore.GetMember", MISSING_CHANNEL_MEMBER_ERROR, nil, "channel_id="+channelId+"user_id="+userId+","+err.Error(), http.StatusNotFound)
} else { } else {
result.Err = model.NewLocAppError("SqlChannelStore.GetMember", "store.sql_channel.get_member.app_error", nil, "channel_id="+channelId+"user_id="+userId+","+err.Error()) result.Err = model.NewLocAppError("SqlChannelStore.GetMember", "store.sql_channel.get_member.app_error", nil, "channel_id="+channelId+"user_id="+userId+","+err.Error())
} }

Просмотреть файл

@@ -79,8 +79,8 @@ func TestChannelStoreSaveDirectChannel(t *testing.T) {
t.Fatal("couldn't save direct channel", err) t.Fatal("couldn't save direct channel", err)
} }
members := (<-store.Channel().GetMembers(o1.Id)).Data.([]model.ChannelMember) members := (<-store.Channel().GetMembers(o1.Id, 0, 100)).Data.(*model.ChannelMembers)
if len(members) != 2 { if len(*members) != 2 {
t.Fatal("should have saved 2 members") t.Fatal("should have saved 2 members")
} }
@@ -135,8 +135,8 @@ func TestChannelStoreCreateDirectChannel(t *testing.T) {
c1 := res.Data.(*model.Channel) c1 := res.Data.(*model.Channel)
members := (<-store.Channel().GetMembers(c1.Id)).Data.([]model.ChannelMember) members := (<-store.Channel().GetMembers(c1.Id, 0, 100)).Data.(*model.ChannelMembers)
if len(members) != 2 { if len(*members) != 2 {
t.Fatal("should have saved 2 members") t.Fatal("should have saved 2 members")
} }
} }

Просмотреть файл

@@ -105,7 +105,7 @@ type ChannelStore interface {
GetForPost(postId string) StoreChannel GetForPost(postId string) StoreChannel
SaveMember(member *model.ChannelMember) StoreChannel SaveMember(member *model.ChannelMember) StoreChannel
UpdateMember(member *model.ChannelMember) StoreChannel UpdateMember(member *model.ChannelMember) StoreChannel
GetMembers(channelId string) StoreChannel GetMembers(channelId string, offset, limit int) StoreChannel
GetMember(channelId string, userId string) StoreChannel GetMember(channelId string, userId string) StoreChannel
GetAllChannelMembersForUser(userId string, allowFromCache bool) StoreChannel GetAllChannelMembersForUser(userId string, allowFromCache bool) StoreChannel
InvalidateAllChannelMembersForUser(userId string) InvalidateAllChannelMembersForUser(userId string)