From 01aaccb34080ede234602d1ca9acee8373b8560f Mon Sep 17 00:00:00 2001 From: Saturnino Abril Date: Mon, 27 Mar 2017 20:41:40 +0900 Subject: APIv4 post /channels/ids (#5845) * APIv4 post /channels/ids * updated enpoint as /teams/{team_id}/channels/ids --- api4/channel.go | 37 ++++++++++++++++++- api4/channel_test.go | 63 ++++++++++++++++++++++++++++++++ app/channel.go | 8 ++++ i18n/en.json | 8 ++++ model/client4.go | 16 ++++++-- store/sql_channel_store.go | 50 +++++++++++++++++++++++++ store/sql_channel_store_test.go | 81 +++++++++++++++++++++++++++++++++++++++++ store/store.go | 1 + 8 files changed, 259 insertions(+), 5 deletions(-) diff --git a/api4/channel.go b/api4/channel.go index a19f7d858..278bf1d2e 100644 --- a/api4/channel.go +++ b/api4/channel.go @@ -19,8 +19,9 @@ func InitChannel() { BaseRoutes.Channels.Handle("/direct", ApiSessionRequired(createDirectChannel)).Methods("POST") 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.ChannelsForTeam.Handle("", ApiSessionRequired(getPublicChannelsForTeam)).Methods("GET") + BaseRoutes.ChannelsForTeam.Handle("/ids", ApiSessionRequired(getPublicChannelsByIdsForTeam)).Methods("POST") + BaseRoutes.ChannelsForTeam.Handle("/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") @@ -322,6 +323,38 @@ func getPublicChannelsForTeam(c *Context, w http.ResponseWriter, r *http.Request } } +func getPublicChannelsByIdsForTeam(c *Context, w http.ResponseWriter, r *http.Request) { + c.RequireTeamId() + if c.Err != nil { + return + } + + channelIds := model.ArrayFromJson(r.Body) + if len(channelIds) == 0 { + c.SetInvalidParam("channel_ids") + return + } + + for _, cid := range channelIds { + if len(cid) != 26 { + c.SetInvalidParam("channel_id") + return + } + } + + if !app.SessionHasPermissionToTeam(c.Session, c.Params.TeamId, model.PERMISSION_VIEW_TEAM) { + c.SetPermissionError(model.PERMISSION_VIEW_TEAM) + return + } + + if channels, err := app.GetPublicChannelsByIdsForTeam(c.Params.TeamId, channelIds); err != nil { + c.Err = err + return + } else { + w.Write([]byte(channels.ToJson())) + } +} + func getChannelsForTeamForUser(c *Context, w http.ResponseWriter, r *http.Request) { c.RequireUserId().RequireTeamId() if c.Err != nil { diff --git a/api4/channel_test.go b/api4/channel_test.go index a208313df..3d501b313 100644 --- a/api4/channel_test.go +++ b/api4/channel_test.go @@ -6,6 +6,7 @@ package api4 import ( "fmt" "net/http" + "sort" "strconv" "testing" @@ -494,6 +495,68 @@ func TestGetPublicChannelsForTeam(t *testing.T) { CheckNoError(t, resp) } +func TestGetPublicChannelsByIdsForTeam(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer TearDown() + Client := th.Client + teamId := th.BasicTeam.Id + input := []string{th.BasicChannel.Id} + output := []string{th.BasicChannel.DisplayName} + + channels, resp := Client.GetPublicChannelsByIdsForTeam(teamId, input) + CheckNoError(t, resp) + + if len(*channels) != 1 { + t.Fatal("should return 1 channel") + } + + if (*channels)[0].DisplayName != output[0] { + t.Fatal("missing channel") + } + + input = append(input, GenerateTestId()) + input = append(input, th.BasicChannel2.Id) + input = append(input, th.BasicPrivateChannel.Id) + output = append(output, th.BasicChannel2.DisplayName) + sort.Strings(output) + + channels, resp = Client.GetPublicChannelsByIdsForTeam(teamId, input) + CheckNoError(t, resp) + + if len(*channels) != 2 { + t.Fatal("should return 2 channels") + } + + for i, c := range *channels { + if c.DisplayName != output[i] { + t.Fatal("missing channel") + } + } + + _, resp = Client.GetPublicChannelsByIdsForTeam(GenerateTestId(), input) + CheckForbiddenStatus(t, resp) + + _, resp = Client.GetPublicChannelsByIdsForTeam(teamId, []string{}) + CheckBadRequestStatus(t, resp) + + _, resp = Client.GetPublicChannelsByIdsForTeam(teamId, []string{"junk"}) + CheckBadRequestStatus(t, resp) + + _, resp = Client.GetPublicChannelsByIdsForTeam(teamId, []string{GenerateTestId()}) + CheckNotFoundStatus(t, resp) + + _, resp = Client.GetPublicChannelsByIdsForTeam(teamId, []string{th.BasicPrivateChannel.Id}) + CheckNotFoundStatus(t, resp) + + Client.Logout() + + _, resp = Client.GetPublicChannelsByIdsForTeam(teamId, input) + CheckUnauthorizedStatus(t, resp) + + _, resp = th.SystemAdminClient.GetPublicChannelsByIdsForTeam(teamId, input) + CheckNoError(t, resp) +} + func TestGetChannelsForTeamForUser(t *testing.T) { th := Setup().InitBasic().InitSystemAdmin() defer TearDown() diff --git a/app/channel.go b/app/channel.go index 51688ad87..5278abbfe 100644 --- a/app/channel.go +++ b/app/channel.go @@ -676,6 +676,14 @@ func GetChannelsUserNotIn(teamId string, userId string, offset int, limit int) ( } } +func GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) (*model.ChannelList, *model.AppError) { + if result := <-Srv.Store.Channel().GetPublicChannelsByIdsForTeam(teamId, channelIds); result.Err != nil { + return nil, result.Err + } else { + return result.Data.(*model.ChannelList), nil + } +} + func GetPublicChannelsForTeam(teamId string, offset int, limit int) (*model.ChannelList, *model.AppError) { if result := <-Srv.Store.Channel().GetPublicChannelsForTeam(teamId, offset, limit); result.Err != nil { return nil, result.Err diff --git a/i18n/en.json b/i18n/en.json index d16a288da..a48cc2cb0 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -4707,6 +4707,14 @@ "id": "store.sql_channel.get_channels.not_found.app_error", "translation": "No channels were found" }, + { + "id": "store.sql_channel.get_channels_by_ids.get.app_error", + "translation": "We couldn't get the channels" + }, + { + "id": "store.sql_channel.get_channels_by_ids.not_found.app_error", + "translation": "No channel found" + }, { "id": "store.sql_channel.get_deleted_by_name.existing.app_error", "translation": "We couldn't find the existing deleted channel" diff --git a/model/client4.go b/model/client4.go index 19d4a9a00..031973a34 100644 --- a/model/client4.go +++ b/model/client4.go @@ -98,7 +98,7 @@ func (c *Client4) GetChannelsRoute() string { return fmt.Sprintf("/channels") } -func (c *Client4) GetPublicChannelsForTeamRoute(teamId string) string { +func (c *Client4) GetChannelsForTeamRoute(teamId string) string { return fmt.Sprintf(c.GetTeamRoute(teamId) + "/channels") } @@ -962,7 +962,17 @@ func (c *Client4) GetChannelStats(channelId string, etag string) (*ChannelStats, // 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 { + if r, err := c.DoApiGet(c.GetChannelsForTeamRoute(teamId)+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return ChannelListFromJson(r.Body), BuildResponse(r) + } +} + +// GetPublicChannelsByIdsForTeam returns a list of public channels based on provided team id string +func (c *Client4) GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) (*ChannelList, *Response) { + if r, err := c.DoApiPost(c.GetChannelsForTeamRoute(teamId)+"/ids", ArrayToJson(channelIds)); err != nil { return nil, &Response{StatusCode: r.StatusCode, Error: err} } else { defer closeBody(r) @@ -982,7 +992,7 @@ func (c *Client4) GetChannelsForTeamForUser(teamId, userId, etag string) (*Chann // 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 { + if r, err := c.DoApiPost(c.GetChannelsForTeamRoute(teamId)+"/search", search.ToJson()); err != nil { return nil, &Response{StatusCode: r.StatusCode, Error: err} } else { defer closeBody(r) diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go index d72722f7c..309337e50 100644 --- a/store/sql_channel_store.go +++ b/store/sql_channel_store.go @@ -619,6 +619,56 @@ func (s SqlChannelStore) GetPublicChannelsForTeam(teamId string, offset int, lim return storeChannel } +func (s SqlChannelStore) GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) StoreChannel { + storeChannel := make(StoreChannel, 1) + + go func() { + result := StoreResult{} + + props := make(map[string]interface{}) + props["teamId"] = teamId + + idQuery := "" + + for index, channelId := range channelIds { + if len(idQuery) > 0 { + idQuery += ", " + } + + props["channelId"+strconv.Itoa(index)] = channelId + idQuery += ":channelId" + strconv.Itoa(index) + } + + data := &model.ChannelList{} + _, err := s.GetReplica().Select(data, + `SELECT + * + FROM + Channels + WHERE + TeamId = :teamId + AND Type = 'O' + AND DeleteAt = 0 + AND Id IN (`+idQuery+`) + ORDER BY DisplayName`, + props) + + if err != nil { + result.Err = model.NewLocAppError("SqlChannelStore.GetPublicChannelsByIdsForTeam", "store.sql_channel.get_channels_by_ids.get.app_error", nil, err.Error()) + } + + if len(*data) == 0 { + result.Err = model.NewAppError("SqlChannelStore.GetPublicChannelsByIdsForTeam", "store.sql_channel.get_channels_by_ids.not_found.app_error", nil, "", http.StatusNotFound) + } + + result.Data = data + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + type channelIdWithCountAndUpdateAt struct { Id string TotalMsgCount int64 diff --git a/store/sql_channel_store_test.go b/store/sql_channel_store_test.go index f347fa438..3cc6f3bc4 100644 --- a/store/sql_channel_store_test.go +++ b/store/sql_channel_store_test.go @@ -934,6 +934,87 @@ func TestChannelStoreGetPublicChannelsForTeam(t *testing.T) { } } +func TestChannelStoreGetPublicChannelsByIdsForTeam(t *testing.T) { + Setup() + + teamId1 := model.NewId() + + oc1 := model.Channel{} + oc1.TeamId = teamId1 + oc1.DisplayName = "OpenChannel1Team1" + oc1.Name = "a" + model.NewId() + "b" + oc1.Type = model.CHANNEL_OPEN + Must(store.Channel().Save(&oc1)) + + oc2 := model.Channel{} + oc2.TeamId = model.NewId() + oc2.DisplayName = "OpenChannel2TeamOther" + oc2.Name = "a" + model.NewId() + "b" + oc2.Type = model.CHANNEL_OPEN + Must(store.Channel().Save(&oc2)) + + pc3 := model.Channel{} + pc3.TeamId = teamId1 + pc3.DisplayName = "PrivateChannel3Team1" + pc3.Name = "a" + model.NewId() + "b" + pc3.Type = model.CHANNEL_PRIVATE + Must(store.Channel().Save(&pc3)) + + cids := []string{oc1.Id} + cresult := <-store.Channel().GetPublicChannelsByIdsForTeam(teamId1, cids) + list := cresult.Data.(*model.ChannelList) + + if len(*list) != 1 { + t.Fatal("should return 1 channel") + } + + if (*list)[0].Id != oc1.Id { + t.Fatal("missing channel") + } + + cids = append(cids, oc2.Id) + cids = append(cids, model.NewId()) + cids = append(cids, pc3.Id) + cresult = <-store.Channel().GetPublicChannelsByIdsForTeam(teamId1, cids) + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 1 { + t.Fatal("should return 1 channel") + } + + oc4 := model.Channel{} + oc4.TeamId = teamId1 + oc4.DisplayName = "OpenChannel4Team1" + oc4.Name = "a" + model.NewId() + "b" + oc4.Type = model.CHANNEL_OPEN + Must(store.Channel().Save(&oc4)) + + cids = append(cids, oc4.Id) + cresult = <-store.Channel().GetPublicChannelsByIdsForTeam(teamId1, cids) + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 2 { + t.Fatal("should return 2 channels") + } + + if (*list)[0].Id != oc1.Id { + t.Fatal("missing channel") + } + + if (*list)[1].Id != oc4.Id { + t.Fatal("missing channel") + } + + cids = cids[:0] + cids = append(cids, model.NewId()) + cresult = <-store.Channel().GetPublicChannelsByIdsForTeam(teamId1, cids) + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 0 { + t.Fatal("should not return a channel") + } +} + func TestChannelStoreGetChannelCounts(t *testing.T) { Setup() diff --git a/store/store.go b/store/store.go index 323727697..528b26a3f 100644 --- a/store/store.go +++ b/store/store.go @@ -103,6 +103,7 @@ type ChannelStore interface { GetChannels(teamId string, userId string) StoreChannel GetMoreChannels(teamId string, userId string, offset int, limit int) StoreChannel GetPublicChannelsForTeam(teamId string, offset int, limit int) StoreChannel + GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) StoreChannel GetChannelCounts(teamId string, userId string) StoreChannel GetTeamChannels(teamId string) StoreChannel GetAll(teamId string) StoreChannel -- cgit v1.2.3-1-g7c22