Implement some channel endpoints for APIv4 (#5846)
* Add v4 endpoint for getting the channels on a team for a user
* Implement PUT /channels/{channel_id}/patch endpoint for APIv4
* Implement POST /teams/{team_id}/channels/search endpoint for APIv4
* Update permission check
Этот коммит содержится в:
коммит произвёл
Corey Hulen
родитель
22715a31ed
Коммит
28a78d7607
@@ -20,9 +20,12 @@ func InitChannel() {
|
||||
BaseRoutes.Channels.Handle("/members/{user_id:[A-Za-z0-9]+}/view", ApiSessionRequired(viewChannel)).Methods("POST")
|
||||
|
||||
BaseRoutes.Team.Handle("/channels", ApiSessionRequired(getPublicChannelsForTeam)).Methods("GET")
|
||||
BaseRoutes.Team.Handle("/channels/search", ApiSessionRequired(searchChannelsForTeam)).Methods("POST")
|
||||
BaseRoutes.User.Handle("/teams/{team_id:[A-Za-z0-9]+}/channels", ApiSessionRequired(getChannelsForTeamForUser)).Methods("GET")
|
||||
|
||||
BaseRoutes.Channel.Handle("", ApiSessionRequired(getChannel)).Methods("GET")
|
||||
BaseRoutes.Channel.Handle("", ApiSessionRequired(updateChannel)).Methods("PUT")
|
||||
BaseRoutes.Channel.Handle("/patch", ApiSessionRequired(patchChannel)).Methods("PUT")
|
||||
BaseRoutes.Channel.Handle("", ApiSessionRequired(deleteChannel)).Methods("DELETE")
|
||||
BaseRoutes.Channel.Handle("/stats", ApiSessionRequired(getChannelStats)).Methods("GET")
|
||||
|
||||
@@ -141,6 +144,37 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func patchChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireChannelId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
patch := model.ChannelPatchFromJson(r.Body)
|
||||
if patch == nil {
|
||||
c.SetInvalidParam("channel")
|
||||
return
|
||||
}
|
||||
|
||||
oldChannel, err := app.GetChannel(c.Params.ChannelId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if !CanManageChannel(c, oldChannel) {
|
||||
return
|
||||
}
|
||||
|
||||
if rchannel, err := app.PatchChannel(oldChannel, patch); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
c.LogAudit("")
|
||||
w.Write([]byte(rchannel.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func CanManageChannel(c *Context, channel *model.Channel) bool {
|
||||
if channel.Type == model.CHANNEL_OPEN && !app.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES)
|
||||
@@ -288,6 +322,63 @@ func getPublicChannelsForTeam(c *Context, w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
}
|
||||
|
||||
func getChannelsForTeamForUser(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireUserId().RequireTeamId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !app.SessionHasPermissionToUser(c.Session, c.Params.UserId) {
|
||||
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
|
||||
return
|
||||
}
|
||||
|
||||
if !app.SessionHasPermissionToTeam(c.Session, c.Params.TeamId, model.PERMISSION_VIEW_TEAM) {
|
||||
c.SetPermissionError(model.PERMISSION_VIEW_TEAM)
|
||||
return
|
||||
}
|
||||
|
||||
if channels, err := app.GetChannelsForUser(c.Params.TeamId, c.Params.UserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else if HandleEtag(channels.Etag(), "Get Channels", w, r) {
|
||||
return
|
||||
} else {
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, channels.Etag())
|
||||
w.Write([]byte(channels.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func searchChannelsForTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireTeamId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
props := model.ChannelSearchFromJson(r.Body)
|
||||
if props == nil {
|
||||
c.SetInvalidParam("channel_search")
|
||||
return
|
||||
}
|
||||
|
||||
if len(props.Term) == 0 {
|
||||
c.SetInvalidParam("term")
|
||||
return
|
||||
}
|
||||
|
||||
if !app.SessionHasPermissionToTeam(c.Session, c.Params.TeamId, model.PERMISSION_LIST_TEAM_CHANNELS) {
|
||||
c.SetPermissionError(model.PERMISSION_LIST_TEAM_CHANNELS)
|
||||
return
|
||||
}
|
||||
|
||||
if channels, err := app.SearchChannels(c.Params.TeamId, props.Term); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(channels.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireChannelId()
|
||||
if c.Err != nil {
|
||||
|
||||
@@ -257,6 +257,62 @@ func TestUpdateChannel(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestPatchChannel(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer TearDown()
|
||||
Client := th.Client
|
||||
|
||||
patch := &model.ChannelPatch{
|
||||
Name: new(string),
|
||||
DisplayName: new(string),
|
||||
Header: new(string),
|
||||
Purpose: new(string),
|
||||
}
|
||||
*patch.Name = model.NewId()
|
||||
*patch.DisplayName = model.NewId()
|
||||
*patch.Header = model.NewId()
|
||||
*patch.Purpose = model.NewId()
|
||||
|
||||
channel, resp := Client.PatchChannel(th.BasicChannel.Id, patch)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
if *patch.Name != channel.Name {
|
||||
t.Fatal("do not match")
|
||||
} else if *patch.DisplayName != channel.DisplayName {
|
||||
t.Fatal("do not match")
|
||||
} else if *patch.Header != channel.Header {
|
||||
t.Fatal("do not match")
|
||||
} else if *patch.Purpose != channel.Purpose {
|
||||
t.Fatal("do not match")
|
||||
}
|
||||
|
||||
patch.Name = nil
|
||||
oldName := channel.Name
|
||||
channel, resp = Client.PatchChannel(th.BasicChannel.Id, patch)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
if channel.Name != oldName {
|
||||
t.Fatal("should not have updated")
|
||||
}
|
||||
|
||||
_, resp = Client.PatchChannel("junk", patch)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
_, resp = Client.PatchChannel(model.NewId(), patch)
|
||||
CheckNotFoundStatus(t, resp)
|
||||
|
||||
user := th.CreateUser()
|
||||
Client.Login(user.Email, user.Password)
|
||||
_, resp = Client.PatchChannel(th.BasicChannel.Id, patch)
|
||||
CheckForbiddenStatus(t, resp)
|
||||
|
||||
_, resp = th.SystemAdminClient.PatchChannel(th.BasicChannel.Id, patch)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
_, resp = th.SystemAdminClient.PatchChannel(th.BasicPrivateChannel.Id, patch)
|
||||
CheckNoError(t, resp)
|
||||
}
|
||||
|
||||
func TestCreateDirectChannel(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer TearDown()
|
||||
@@ -438,6 +494,109 @@ func TestGetPublicChannelsForTeam(t *testing.T) {
|
||||
CheckNoError(t, resp)
|
||||
}
|
||||
|
||||
func TestGetChannelsForTeamForUser(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer TearDown()
|
||||
Client := th.Client
|
||||
|
||||
channels, resp := Client.GetChannelsForTeamForUser(th.BasicTeam.Id, th.BasicUser.Id, "")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
found := make([]bool, 3)
|
||||
for _, c := range *channels {
|
||||
if c.Id == th.BasicChannel.Id {
|
||||
found[0] = true
|
||||
} else if c.Id == th.BasicChannel2.Id {
|
||||
found[1] = true
|
||||
} else if c.Id == th.BasicPrivateChannel.Id {
|
||||
found[2] = true
|
||||
}
|
||||
|
||||
if c.TeamId != th.BasicTeam.Id && c.TeamId != "" {
|
||||
t.Fatal("wrong team")
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range found {
|
||||
if !f {
|
||||
t.Fatal("missing a channel")
|
||||
}
|
||||
}
|
||||
|
||||
channels, resp = Client.GetChannelsForTeamForUser(th.BasicTeam.Id, th.BasicUser.Id, resp.Etag)
|
||||
CheckEtag(t, channels, resp)
|
||||
|
||||
_, resp = Client.GetChannelsForTeamForUser(th.BasicTeam.Id, "junk", "")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
_, resp = Client.GetChannelsForTeamForUser("junk", th.BasicUser.Id, "")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
_, resp = Client.GetChannelsForTeamForUser(th.BasicTeam.Id, th.BasicUser2.Id, "")
|
||||
CheckForbiddenStatus(t, resp)
|
||||
|
||||
_, resp = Client.GetChannelsForTeamForUser(model.NewId(), th.BasicUser.Id, "")
|
||||
CheckForbiddenStatus(t, resp)
|
||||
|
||||
_, resp = th.SystemAdminClient.GetChannelsForTeamForUser(th.BasicTeam.Id, th.BasicUser.Id, "")
|
||||
CheckNoError(t, resp)
|
||||
}
|
||||
|
||||
func TestSearchChannels(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer TearDown()
|
||||
Client := th.Client
|
||||
|
||||
search := &model.ChannelSearch{Term: th.BasicChannel.Name}
|
||||
|
||||
channels, resp := Client.SearchChannels(th.BasicTeam.Id, search)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
found := false
|
||||
for _, c := range *channels {
|
||||
if c.Type != model.CHANNEL_OPEN {
|
||||
t.Fatal("should only return public channels")
|
||||
}
|
||||
|
||||
if c.Id == th.BasicChannel.Id {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatal("didn't find channel")
|
||||
}
|
||||
|
||||
search.Term = th.BasicPrivateChannel.Name
|
||||
channels, resp = Client.SearchChannels(th.BasicTeam.Id, search)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
found = false
|
||||
for _, c := range *channels {
|
||||
if c.Id == th.BasicPrivateChannel.Id {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
t.Fatal("shouldn't find private channel")
|
||||
}
|
||||
|
||||
search.Term = ""
|
||||
_, resp = Client.SearchChannels(th.BasicTeam.Id, search)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
search.Term = th.BasicChannel.Name
|
||||
_, resp = Client.SearchChannels(model.NewId(), search)
|
||||
CheckForbiddenStatus(t, resp)
|
||||
|
||||
_, resp = Client.SearchChannels("junk", search)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
_, resp = th.SystemAdminClient.SearchChannels(th.BasicTeam.Id, search)
|
||||
CheckNoError(t, resp)
|
||||
}
|
||||
|
||||
func TestDeleteChannel(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer TearDown()
|
||||
|
||||
@@ -260,6 +260,11 @@ func UpdateChannel(channel *model.Channel) (*model.Channel, *model.AppError) {
|
||||
}
|
||||
}
|
||||
|
||||
func PatchChannel(channel *model.Channel, patch *model.ChannelPatch) (*model.Channel, *model.AppError) {
|
||||
channel.Patch(patch)
|
||||
return UpdateChannel(channel)
|
||||
}
|
||||
|
||||
func UpdateChannelMemberRoles(channelId string, userId string, newRoles string) (*model.ChannelMember, *model.AppError) {
|
||||
var member *model.ChannelMember
|
||||
var err *model.AppError
|
||||
|
||||
@@ -46,6 +46,13 @@ type Channel struct {
|
||||
CreatorId string `json:"creator_id"`
|
||||
}
|
||||
|
||||
type ChannelPatch struct {
|
||||
DisplayName *string `json:"display_name"`
|
||||
Name *string `json:"name"`
|
||||
Header *string `json:"header"`
|
||||
Purpose *string `json:"purpose"`
|
||||
}
|
||||
|
||||
func (o *Channel) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
@@ -55,6 +62,15 @@ func (o *Channel) ToJson() string {
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ChannelPatch) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func ChannelFromJson(data io.Reader) *Channel {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o Channel
|
||||
@@ -66,6 +82,17 @@ func ChannelFromJson(data io.Reader) *Channel {
|
||||
}
|
||||
}
|
||||
|
||||
func ChannelPatchFromJson(data io.Reader) *ChannelPatch {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o ChannelPatch
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Channel) Etag() string {
|
||||
return Etag(o.Id, o.UpdateAt)
|
||||
}
|
||||
@@ -137,6 +164,24 @@ func (o *Channel) IsGroupOrDirect() bool {
|
||||
return o.Type == CHANNEL_DIRECT || o.Type == CHANNEL_GROUP
|
||||
}
|
||||
|
||||
func (o *Channel) Patch(patch *ChannelPatch) {
|
||||
if patch.DisplayName != nil {
|
||||
o.DisplayName = *patch.DisplayName
|
||||
}
|
||||
|
||||
if patch.Name != nil {
|
||||
o.Name = *patch.Name
|
||||
}
|
||||
|
||||
if patch.Header != nil {
|
||||
o.Header = *patch.Header
|
||||
}
|
||||
|
||||
if patch.Purpose != nil {
|
||||
o.Purpose = *patch.Purpose
|
||||
}
|
||||
}
|
||||
|
||||
func GetDMNameFromIds(userId1, userId2 string) string {
|
||||
if userId1 > userId2 {
|
||||
return userId2 + "__" + userId1
|
||||
|
||||
@@ -16,6 +16,39 @@ func TestChannelJson(t *testing.T) {
|
||||
if o.Id != ro.Id {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
|
||||
p := ChannelPatch{Name: new(string)}
|
||||
*p.Name = NewId()
|
||||
json = p.ToJson()
|
||||
rp := ChannelPatchFromJson(strings.NewReader(json))
|
||||
|
||||
if *p.Name != *rp.Name {
|
||||
t.Fatal("names do not match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelPatch(t *testing.T) {
|
||||
p := &ChannelPatch{Name: new(string), DisplayName: new(string), Header: new(string), Purpose: new(string)}
|
||||
*p.Name = NewId()
|
||||
*p.DisplayName = NewId()
|
||||
*p.Header = NewId()
|
||||
*p.Purpose = NewId()
|
||||
|
||||
o := Channel{Id: NewId(), Name: NewId()}
|
||||
o.Patch(p)
|
||||
|
||||
if *p.Name != o.Name {
|
||||
t.Fatal("do not match")
|
||||
}
|
||||
if *p.DisplayName != o.DisplayName {
|
||||
t.Fatal("do not match")
|
||||
}
|
||||
if *p.Header != o.Header {
|
||||
t.Fatal("do not match")
|
||||
}
|
||||
if *p.Purpose != o.Purpose {
|
||||
t.Fatal("do not match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelIsValid(t *testing.T) {
|
||||
|
||||
@@ -897,6 +897,16 @@ func (c *Client4) UpdateChannel(channel *Channel) (*Channel, *Response) {
|
||||
}
|
||||
}
|
||||
|
||||
// PatchChannel partially updates a channel. Any missing fields are not updated.
|
||||
func (c *Client4) PatchChannel(channelId string, patch *ChannelPatch) (*Channel, *Response) {
|
||||
if r, err := c.DoApiPut(c.GetChannelRoute(channelId)+"/patch", patch.ToJson()); err != nil {
|
||||
return nil, &Response{StatusCode: r.StatusCode, Error: err}
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return ChannelFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateDirectChannel creates a direct message channel based on the two user
|
||||
// ids provided.
|
||||
func (c *Client4) CreateDirectChannel(userId1, userId2 string) (*Channel, *Response) {
|
||||
@@ -929,7 +939,7 @@ func (c *Client4) GetChannelStats(channelId string, etag string) (*ChannelStats,
|
||||
}
|
||||
}
|
||||
|
||||
// GetPublicChannelsForTeam returns a channel based on the provided team id string.
|
||||
// GetPublicChannelsForTeam returns a list of public channels based on the provided team id string.
|
||||
func (c *Client4) GetPublicChannelsForTeam(teamId string, page int, perPage int, etag string) (*ChannelList, *Response) {
|
||||
query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
|
||||
if r, err := c.DoApiGet(c.GetPublicChannelsForTeamRoute(teamId)+query, etag); err != nil {
|
||||
@@ -940,6 +950,26 @@ func (c *Client4) GetPublicChannelsForTeam(teamId string, page int, perPage int,
|
||||
}
|
||||
}
|
||||
|
||||
// GetChannelsForTeamForUser returns a list channels of on a team for a user.
|
||||
func (c *Client4) GetChannelsForTeamForUser(teamId, userId, etag string) (*ChannelList, *Response) {
|
||||
if r, err := c.DoApiGet(c.GetUserRoute(userId)+c.GetTeamRoute(teamId)+"/channels", etag); err != nil {
|
||||
return nil, &Response{StatusCode: r.StatusCode, Error: err}
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return ChannelListFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// SearchChannels returns the channels on a team matching the provided search term.
|
||||
func (c *Client4) SearchChannels(teamId string, search *ChannelSearch) (*ChannelList, *Response) {
|
||||
if r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/channels/search", search.ToJson()); err != nil {
|
||||
return nil, &Response{StatusCode: r.StatusCode, Error: err}
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return ChannelListFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteChannel deletes channel based on the provided channel id string.
|
||||
func (c *Client4) DeleteChannel(channelId string) (bool, *Response) {
|
||||
if r, err := c.DoApiDelete(c.GetChannelRoute(channelId)); err != nil {
|
||||
|
||||
Ссылка в новой задаче
Block a user