[MM-23719] api4/channel: add move channel to a team endpoint (#14246)

* api4: add move channel method

* api4: add tests for move channel, model: add move channel to client4.go

* add api.channel.move_channel.type.invalid message

* model/client4: remove a redundant line

* api4/channel: add tests for gm and private channel types

* app/channel: update move channel comment

* app/channel: add extra check if a users joins to channel during movement

* app/channel: log errors for post move channel

* app/channel: remove deactivated members by default while moving a ch.

* model/client: update move channel command

* fix vet errors

* app/channel: add missing webhook updates

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Ibrahim Serdar Acikgoz
2020-06-22 16:57:49 +03:00
коммит произвёл GitHub
родитель 0e714f350a
Коммит 124014ad9c
9 изменённых файлов: 296 добавлений и 27 удалений

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

@@ -45,6 +45,7 @@ func (api *API) InitChannel() {
api.BaseRoutes.Channel.Handle("/pinned", api.ApiSessionRequired(getPinnedPosts)).Methods("GET") api.BaseRoutes.Channel.Handle("/pinned", api.ApiSessionRequired(getPinnedPosts)).Methods("GET")
api.BaseRoutes.Channel.Handle("/timezones", api.ApiSessionRequired(getChannelMembersTimezones)).Methods("GET") api.BaseRoutes.Channel.Handle("/timezones", api.ApiSessionRequired(getChannelMembersTimezones)).Methods("GET")
api.BaseRoutes.Channel.Handle("/members_minus_group_members", api.ApiSessionRequired(channelMembersMinusGroupMembers)).Methods("GET") api.BaseRoutes.Channel.Handle("/members_minus_group_members", api.ApiSessionRequired(channelMembersMinusGroupMembers)).Methods("GET")
api.BaseRoutes.Channel.Handle("/move", api.ApiSessionRequired(moveChannel)).Methods("POST")
api.BaseRoutes.Channel.Handle("/member_counts_by_group", api.ApiSessionRequired(channelMemberCountsByGroup)).Methods("GET") api.BaseRoutes.Channel.Handle("/member_counts_by_group", api.ApiSessionRequired(channelMemberCountsByGroup)).Methods("GET")
api.BaseRoutes.ChannelForUser.Handle("/unread", api.ApiSessionRequired(getChannelUnread)).Methods("GET") api.BaseRoutes.ChannelForUser.Handle("/unread", api.ApiSessionRequired(getChannelUnread)).Methods("GET")
@@ -1753,3 +1754,70 @@ func patchChannelModerations(c *Context, w http.ResponseWriter, r *http.Request)
auditRec.Success() auditRec.Success()
w.Write(b) w.Write(b)
} }
func moveChannel(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireChannelId()
if c.Err != nil {
return
}
channel, err := c.App.GetChannel(c.Params.ChannelId)
if err != nil {
c.Err = err
return
}
props := model.StringInterfaceFromJson(r.Body)
teamId, ok := props["team_id"].(string)
if !ok {
c.SetInvalidParam("team_id")
return
}
team, err := c.App.GetTeam(teamId)
if err != nil {
c.Err = err
return
}
auditRec := c.MakeAuditRecord("moveChannel", audit.Fail)
defer c.LogAuditRec(auditRec)
auditRec.AddMeta("channel_id", channel.Id)
auditRec.AddMeta("channel_name", channel.Name)
auditRec.AddMeta("team_id", team.Id)
auditRec.AddMeta("team_name", team.Name)
if channel.Type == model.CHANNEL_DIRECT || channel.Type == model.CHANNEL_GROUP || channel.Type == model.CHANNEL_PRIVATE {
c.Err = model.NewAppError("moveChannel", "api.channel.move_channel.type.invalid", nil, "", http.StatusForbidden)
return
}
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
}
user, err := c.App.GetUser(c.App.Session().UserId)
if err != nil {
c.Err = err
return
}
err = c.App.RemoveAllDeactivatedMembersFromChannel(channel)
if err != nil {
c.Err = err
return
}
err = c.App.MoveChannel(team, channel, user)
if err != nil {
c.Err = err
return
}
auditRec.Success()
c.LogAudit("channel=" + channel.Name)
c.LogAudit("team=" + team.Name)
w.Write([]byte(channel.ToJson()))
}

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

@@ -3790,3 +3790,67 @@ func TestGetChannelMemberCountsByGroup(t *testing.T) {
require.ElementsMatch(t, expectedMemberCounts, memberCounts) require.ElementsMatch(t, expectedMemberCounts, memberCounts)
}) })
} }
func TestMoveChannel(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
Client := th.Client
team1 := th.BasicTeam
team2 := th.CreateTeam()
t.Run("Should move channel", func(t *testing.T) {
publicChannel := th.CreatePublicChannel()
ch, resp := th.SystemAdminClient.MoveChannel(publicChannel.Id, team2.Id)
require.Nil(t, resp.Error)
require.Equal(t, team2.Id, ch.TeamId)
})
t.Run("Should fail when trying to move a private channel", func(t *testing.T) {
channel := th.CreatePrivateChannel()
_, resp := Client.MoveChannel(channel.Id, team1.Id)
require.NotNil(t, resp.Error)
CheckErrorMessage(t, resp, "api.channel.move_channel.type.invalid")
})
t.Run("Should fail when trying to move a DM channel", func(t *testing.T) {
user := th.CreateUser()
dmChannel := th.CreateDmChannel(user)
_, resp := Client.MoveChannel(dmChannel.Id, team1.Id)
require.NotNil(t, resp.Error)
CheckErrorMessage(t, resp, "api.channel.move_channel.type.invalid")
})
t.Run("Should fail when trying to move a group channel", func(t *testing.T) {
user := th.CreateUser()
gmChannel, err := th.App.CreateGroupChannel([]string{th.BasicUser.Id, th.SystemAdminUser.Id, th.TeamAdminUser.Id}, user.Id)
require.Nil(t, err)
_, resp := Client.MoveChannel(gmChannel.Id, team1.Id)
require.NotNil(t, resp.Error)
CheckErrorMessage(t, resp, "api.channel.move_channel.type.invalid")
})
t.Run("Should fail due to permissions", func(t *testing.T) {
publicChannel := th.CreatePublicChannel()
_, resp := Client.MoveChannel(publicChannel.Id, team1.Id)
require.NotNil(t, resp.Error)
CheckErrorMessage(t, resp, "api.context.permissions.app_error")
})
t.Run("Should fail to move channel due to a member not member of target team", func(t *testing.T) {
publicChannel := th.CreatePublicChannel()
user := th.BasicUser
_, resp := th.SystemAdminClient.RemoveTeamMember(team2.Id, user.Id)
CheckNoError(t, resp)
_, resp = th.SystemAdminClient.AddChannelMember(publicChannel.Id, user.Id)
CheckNoError(t, resp)
_, resp = th.SystemAdminClient.MoveChannel(publicChannel.Id, team2.Id)
require.NotNil(t, resp.Error)
CheckErrorMessage(t, resp, "app.channel.move_channel.members_do_not_match.error")
})
}

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

@@ -222,6 +222,9 @@ type AppIface interface {
MakeAuditRecord(event string, initialStatus string) *audit.Record MakeAuditRecord(event string, initialStatus string) *audit.Record
// MarkChanelAsUnreadFromPost will take a post and set the channel as unread from that one. // MarkChanelAsUnreadFromPost will take a post and set the channel as unread from that one.
MarkChannelAsUnreadFromPost(postID string, userID string) (*model.ChannelUnreadAt, *model.AppError) MarkChannelAsUnreadFromPost(postID string, userID string) (*model.ChannelUnreadAt, *model.AppError)
// MoveChannel method is prone to data races if someone joins to channel during the move process. However this
// function is only exposed to sysadmins and the possibility of this edge case is realtively small.
MoveChannel(team *model.Team, channel *model.Channel, user *model.User) *model.AppError
// NewWebConn returns a new WebConn instance. // NewWebConn returns a new WebConn instance.
NewWebConn(ws *websocket.Conn, session model.Session, t goi18n.TranslateFunc, locale string) *WebConn NewWebConn(ws *websocket.Conn, session model.Session, t goi18n.TranslateFunc, locale string) *WebConn
// NewWebHub creates a new Hub. // NewWebHub creates a new Hub.
@@ -292,9 +295,6 @@ type AppIface interface {
// The result can be used, for example, to determine the set of users who would be removed from a team if the team // The result can be used, for example, to determine the set of users who would be removed from a team if the team
// were group-constrained with the given groups. // were group-constrained with the given groups.
TeamMembersMinusGroupMembers(teamID string, groupIDs []string, page, perPage int) ([]*model.UserWithGroups, int64, *model.AppError) TeamMembersMinusGroupMembers(teamID string, groupIDs []string, page, perPage int) ([]*model.UserWithGroups, int64, *model.AppError)
// This function is intended for use from the CLI. It is not robust against people joining the channel while the move
// is in progress, and therefore should not be used from the API without first fixing this potential race condition.
MoveChannel(team *model.Team, channel *model.Channel, user *model.User, removeDeactivatedMembers bool) *model.AppError
// This function migrates the default built in roles from code/config to the database. // This function migrates the default built in roles from code/config to the database.
DoAdvancedPermissionsMigration() DoAdvancedPermissionsMigration()
// This to be used for places we check the users password when they are already logged in // This to be used for places we check the users password when they are already logged in
@@ -788,6 +788,7 @@ type AppIface interface {
RegenerateTeamInviteId(teamId string) (*model.Team, *model.AppError) RegenerateTeamInviteId(teamId string) (*model.Team, *model.AppError)
RegisterPluginCommand(pluginId string, command *model.Command) error RegisterPluginCommand(pluginId string, command *model.Command) error
ReloadConfig() error ReloadConfig() error
RemoveAllDeactivatedMembersFromChannel(channel *model.Channel) *model.AppError
RemoveConfigListener(id string) RemoveConfigListener(id string)
RemoveFile(path string) *model.AppError RemoveFile(path string) *model.AppError
RemovePlugin(id string) *model.AppError RemovePlugin(id string) *model.AppError

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

@@ -2344,15 +2344,13 @@ func (a *App) PermanentDeleteChannel(channel *model.Channel) *model.AppError {
return nil return nil
} }
// This function is intended for use from the CLI. It is not robust against people joining the channel while the move func (a *App) RemoveAllDeactivatedMembersFromChannel(channel *model.Channel) *model.AppError {
// is in progress, and therefore should not be used from the API without first fixing this potential race condition. return a.Srv().Store.Channel().RemoveAllDeactivatedMembers(channel.Id)
func (a *App) MoveChannel(team *model.Team, channel *model.Channel, user *model.User, removeDeactivatedMembers bool) *model.AppError { }
if removeDeactivatedMembers {
if err := a.Srv().Store.Channel().RemoveAllDeactivatedMembers(channel.Id); err != nil {
return err
}
}
// MoveChannel method is prone to data races if someone joins to channel during the move process. However this
// function is only exposed to sysadmins and the possibility of this edge case is realtively small.
func (a *App) MoveChannel(team *model.Team, channel *model.Channel, user *model.User) *model.AppError {
// Check that all channel members are in the destination team. // Check that all channel members are in the destination team.
channelMembers, err := a.GetChannelMembersPage(channel.Id, 0, 10000000) channelMembers, err := a.GetChannelMembersPage(channel.Id, 0, 10000000)
if err != nil { if err != nil {
@@ -2394,7 +2392,40 @@ func (a *App) MoveChannel(team *model.Team, channel *model.Channel, user *model.
return model.NewAppError("MoveChannel", "app.channel.update_channel.internal_error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("MoveChannel", "app.channel.update_channel.internal_error", nil, err.Error(), http.StatusInternalServerError)
} }
} }
a.postChannelMoveMessage(user, channel, previousTeam)
if incomingWebhooks, err := a.GetIncomingWebhooksForTeamPage(previousTeam.Id, 0, 10000000); err != nil {
mlog.Warn("Failed to get incoming webhooks", mlog.Err(err))
} else {
for _, webhook := range incomingWebhooks {
if webhook.ChannelId == channel.Id {
webhook.TeamId = team.Id
if _, err := a.Srv().Store.Webhook().UpdateIncoming(webhook); err != nil {
mlog.Warn("Failed to move incoming webhook to new team", mlog.String("webhook id", webhook.Id))
}
}
}
}
if outgoingWebhooks, err := a.GetOutgoingWebhooksForTeamPage(previousTeam.Id, 0, 10000000); err != nil {
mlog.Warn("Failed to get outgoing webhooks", mlog.Err(err))
} else {
for _, webhook := range outgoingWebhooks {
if webhook.ChannelId == channel.Id {
webhook.TeamId = team.Id
if _, err := a.Srv().Store.Webhook().UpdateOutgoing(webhook); err != nil {
mlog.Warn("Failed to move outgoing webhook to new team.", mlog.String("webhook id", webhook.Id))
}
}
}
}
if err := a.removeUsersFromChannelNotMemberOfTeam(user, channel, team); err != nil {
mlog.Warn("error while removing non-team member users", mlog.Err(err))
}
if err := a.postChannelMoveMessage(user, channel, previousTeam); err != nil {
mlog.Warn("error while posting move channel message", mlog.Err(err))
}
return nil return nil
} }
@@ -2418,6 +2449,40 @@ func (a *App) postChannelMoveMessage(user *model.User, channel *model.Channel, p
return nil return nil
} }
func (a *App) removeUsersFromChannelNotMemberOfTeam(remover *model.User, channel *model.Channel, team *model.Team) *model.AppError {
channelMembers, err := a.GetChannelMembersPage(channel.Id, 0, 10000000)
if err != nil {
return err
}
channelMemberIds := []string{}
channelMemberMap := make(map[string]struct{})
for _, channelMember := range *channelMembers {
channelMemberMap[channelMember.UserId] = struct{}{}
channelMemberIds = append(channelMemberIds, channelMember.UserId)
}
if len(channelMemberIds) > 0 {
teamMembers, err := a.GetTeamMembersByIds(team.Id, channelMemberIds, nil)
if err != nil {
return err
}
if len(teamMembers) != len(*channelMembers) {
for _, teamMember := range teamMembers {
delete(channelMemberMap, teamMember.UserId)
}
for userId := range channelMemberMap {
if err := a.removeUserFromChannel(userId, remover.Id, channel); err != nil {
return err
}
}
}
}
return nil
}
func (a *App) GetPinnedPosts(channelId string) (*model.PostList, *model.AppError) { func (a *App) GetPinnedPosts(channelId string) (*model.PostList, *model.AppError) {
return a.Srv().Store.Channel().GetPinnedPosts(channelId) return a.Srv().Store.Channel().GetPinnedPosts(channelId)
} }

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

@@ -68,6 +68,40 @@ func TestPermanentDeleteChannel(t *testing.T) {
require.NotNil(t, err, "Outgoing webhook wasn't deleted") require.NotNil(t, err, "Outgoing webhook wasn't deleted")
} }
func TestRemoveAllDeactivatedMembersFromChannel(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
var err *model.AppError
team := th.CreateTeam()
channel := th.CreateChannel(team)
defer func() {
th.App.PermanentDeleteChannel(channel)
th.App.PermanentDeleteTeam(team)
}()
_, err = th.App.AddUserToTeam(team.Id, th.BasicUser.Id, "")
require.Nil(t, err)
deacivatedUser := th.CreateUser()
_, err = th.App.AddUserToTeam(team.Id, deacivatedUser.Id, "")
require.Nil(t, err)
_, err = th.App.AddUserToChannel(deacivatedUser, channel)
require.Nil(t, err)
channelMembers, err := th.App.GetChannelMembersPage(channel.Id, 0, 10000000)
require.Nil(t, err)
require.Len(t, *channelMembers, 2)
_, err = th.App.UpdateActive(deacivatedUser, false)
require.Nil(t, err)
err = th.App.RemoveAllDeactivatedMembersFromChannel(channel)
require.Nil(t, err)
channelMembers, err = th.App.GetChannelMembersPage(channel.Id, 0, 10000000)
require.Nil(t, err)
require.Len(t, *channelMembers, 1)
}
func TestMoveChannel(t *testing.T) { func TestMoveChannel(t *testing.T) {
th := Setup(t).InitBasic() th := Setup(t).InitBasic()
defer th.TearDown() defer th.TearDown()
@@ -97,13 +131,13 @@ func TestMoveChannel(t *testing.T) {
_, err = th.App.AddUserToChannel(th.BasicUser2, channel1) _, err = th.App.AddUserToChannel(th.BasicUser2, channel1)
require.Nil(t, err) require.Nil(t, err)
err = th.App.MoveChannel(targetTeam, channel1, th.BasicUser, false) err = th.App.MoveChannel(targetTeam, channel1, th.BasicUser)
require.NotNil(t, err, "Should have failed due to mismatched members.") require.NotNil(t, err, "Should have failed due to mismatched members.")
_, err = th.App.AddUserToTeam(targetTeam.Id, th.BasicUser2.Id, "") _, err = th.App.AddUserToTeam(targetTeam.Id, th.BasicUser2.Id, "")
require.Nil(t, err) require.Nil(t, err)
err = th.App.MoveChannel(targetTeam, channel1, th.BasicUser, false) err = th.App.MoveChannel(targetTeam, channel1, th.BasicUser)
require.Nil(t, err) require.Nil(t, err)
// Test moving a channel with a deactivated user who isn't in the destination team. // Test moving a channel with a deactivated user who isn't in the destination team.
@@ -123,12 +157,9 @@ func TestMoveChannel(t *testing.T) {
_, err = th.App.UpdateActive(deacivatedUser, false) _, err = th.App.UpdateActive(deacivatedUser, false)
require.Nil(t, err) require.Nil(t, err)
err = th.App.MoveChannel(targetTeam, channel2, th.BasicUser, false) err = th.App.MoveChannel(targetTeam, channel2, th.BasicUser)
require.NotNil(t, err, "Should have failed due to mismatched deacivated member.") require.NotNil(t, err, "Should have failed due to mismatched deacivated member.")
err = th.App.MoveChannel(targetTeam, channel2, th.BasicUser, true)
require.Nil(t, err)
// Test moving a channel with no members. // Test moving a channel with no members.
channel3 := &model.Channel{ channel3 := &model.Channel{
DisplayName: "dn_" + model.NewId(), DisplayName: "dn_" + model.NewId(),
@@ -142,7 +173,7 @@ func TestMoveChannel(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
defer th.App.PermanentDeleteChannel(channel3) defer th.App.PermanentDeleteChannel(channel3)
err = th.App.MoveChannel(targetTeam, channel3, th.BasicUser, false) err = th.App.MoveChannel(targetTeam, channel3, th.BasicUser)
assert.Nil(t, err) assert.Nil(t, err)
} }

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

@@ -9966,7 +9966,7 @@ func (a *OpenTracingAppLayer) MigrateFilenamesToFileInfos(post *model.Post) []*m
return resultVar0 return resultVar0
} }
func (a *OpenTracingAppLayer) MoveChannel(team *model.Team, channel *model.Channel, user *model.User, removeDeactivatedMembers bool) *model.AppError { func (a *OpenTracingAppLayer) MoveChannel(team *model.Team, channel *model.Channel, user *model.User) *model.AppError {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.MoveChannel") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.MoveChannel")
@@ -9978,7 +9978,7 @@ func (a *OpenTracingAppLayer) MoveChannel(team *model.Team, channel *model.Chann
}() }()
defer span.Finish() defer span.Finish()
resultVar0 := a.app.MoveChannel(team, channel, user, removeDeactivatedMembers) resultVar0 := a.app.MoveChannel(team, channel, user)
if resultVar0 != nil { if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0)) span.LogFields(spanlog.Error(resultVar0))
@@ -11029,6 +11029,28 @@ func (a *OpenTracingAppLayer) ReloadConfig() error {
return resultVar0 return resultVar0
} }
func (a *OpenTracingAppLayer) RemoveAllDeactivatedMembersFromChannel(channel *model.Channel) *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.RemoveAllDeactivatedMembersFromChannel")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0 := a.app.RemoveAllDeactivatedMembersFromChannel(channel)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (a *OpenTracingAppLayer) RemoveConfigListener(id string) { func (a *OpenTracingAppLayer) RemoveConfigListener(id string) {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.RemoveConfigListener") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.RemoveConfigListener")

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

@@ -138,7 +138,6 @@ func init() {
ChannelCreateCmd.Flags().Bool("private", false, "Create a private channel.") ChannelCreateCmd.Flags().Bool("private", false, "Create a private channel.")
MoveChannelsCmd.Flags().String("username", "", "Required. Username who is moving the channel.") MoveChannelsCmd.Flags().String("username", "", "Required. Username who is moving the channel.")
MoveChannelsCmd.Flags().Bool("remove-deactivated-users", false, "Automatically remove any deactivated users from the channel before moving it.")
DeleteChannelsCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the channels.") DeleteChannelsCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the channels.")
@@ -404,8 +403,6 @@ func moveChannelsCmdF(command *cobra.Command, args []string) error {
} }
user := getUserFromUserArg(a, username) user := getUserFromUserArg(a, username)
removeDeactivatedMembers, _ := command.Flags().GetBool("remove-deactivated-users")
channels := getChannelsFromChannelArgs(a, args[1:]) channels := getChannelsFromChannelArgs(a, args[1:])
for i, channel := range channels { for i, channel := range channels {
if channel == nil { if channel == nil {
@@ -413,7 +410,7 @@ func moveChannelsCmdF(command *cobra.Command, args []string) error {
continue continue
} }
originTeamID := channel.TeamId originTeamID := channel.TeamId
if err := moveChannel(a, team, channel, user, removeDeactivatedMembers); err != nil { if err := moveChannel(a, team, channel, user); err != nil {
CommandPrintErrorln("Unable to move channel '" + channel.Name + "' error: " + err.Error()) CommandPrintErrorln("Unable to move channel '" + channel.Name + "' error: " + err.Error())
} else { } else {
CommandPrettyPrintln("Moved channel '" + channel.Name + "' to " + team.Name + "(" + team.Id + ") from " + originTeamID + ".") CommandPrettyPrintln("Moved channel '" + channel.Name + "' to " + team.Name + "(" + team.Id + ") from " + originTeamID + ".")
@@ -422,10 +419,14 @@ func moveChannelsCmdF(command *cobra.Command, args []string) error {
return nil return nil
} }
func moveChannel(a *app.App, team *model.Team, channel *model.Channel, user *model.User, removeDeactivatedMembers bool) *model.AppError { func moveChannel(a *app.App, team *model.Team, channel *model.Channel, user *model.User) *model.AppError {
oldTeamId := channel.TeamId oldTeamId := channel.TeamId
if err := a.MoveChannel(team, channel, user, removeDeactivatedMembers); err != nil { if err := a.RemoveAllDeactivatedMembersFromChannel(channel); err != nil {
return err
}
if err := a.MoveChannel(team, channel, user); err != nil {
return err return err
} }

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

@@ -331,6 +331,10 @@
"id": "api.channel.leave.left", "id": "api.channel.leave.left",
"translation": "%v left the channel." "translation": "%v left the channel."
}, },
{
"id": "api.channel.move_channel.type.invalid",
"translation": "Unable to move direct or group message channels"
},
{ {
"id": "api.channel.patch_channel_moderations.license.error", "id": "api.channel.patch_channel_moderations.license.error",
"translation": "Your license does not support channel moderation" "translation": "Your license does not support channel moderation"

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

@@ -2456,6 +2456,19 @@ func (c *Client4) DeleteChannel(channelId string) (bool, *Response) {
return CheckStatusOK(r), BuildResponse(r) return CheckStatusOK(r), BuildResponse(r)
} }
// MoveChannel moves the channel to the destination team.
func (c *Client4) MoveChannel(channelId, teamId string) (*Channel, *Response) {
requestBody := map[string]string{
"team_id": teamId,
}
r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/move", MapToJson(requestBody))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return ChannelFromJson(r.Body), BuildResponse(r)
}
// GetChannelByName returns a channel based on the provided channel name and team id strings. // GetChannelByName returns a channel based on the provided channel name and team id strings.
func (c *Client4) GetChannelByName(channelName, teamId string, etag string) (*Channel, *Response) { func (c *Client4) GetChannelByName(channelName, teamId string, etag string) (*Channel, *Response) {
r, err := c.DoApiGet(c.GetChannelByNameRoute(channelName, teamId), etag) r, err := c.DoApiGet(c.GetChannelByNameRoute(channelName, teamId), etag)