[MM-22133] Allow exporting and importing archived channels (#23724)
Этот коммит содержится в:
@@ -97,17 +97,17 @@ func (a *App) BulkExport(ctx request.CTX, writer io.Writer, outPath string, job
|
||||
}
|
||||
|
||||
ctx.Logger().Info("Bulk export: exporting channels")
|
||||
if err = a.exportAllChannels(ctx, job, writer, teamNames); err != nil {
|
||||
if err = a.exportAllChannels(ctx, job, writer, teamNames, opts.IncludeArchivedChannels); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.Logger().Info("Bulk export: exporting users")
|
||||
if err = a.exportAllUsers(ctx, job, writer); err != nil {
|
||||
if err = a.exportAllUsers(ctx, job, writer, opts.IncludeArchivedChannels); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.Logger().Info("Bulk export: exporting posts")
|
||||
attachments, err := a.exportAllPosts(ctx, job, writer, opts.IncludeAttachments)
|
||||
attachments, err := a.exportAllPosts(ctx, job, writer, opts.IncludeAttachments, opts.IncludeArchivedChannels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -219,7 +219,7 @@ func (a *App) exportAllTeams(ctx request.CTX, job *model.Job, writer io.Writer)
|
||||
return teamNames, nil
|
||||
}
|
||||
|
||||
func (a *App) exportAllChannels(ctx request.CTX, job *model.Job, writer io.Writer, teamNames map[string]bool) *model.AppError {
|
||||
func (a *App) exportAllChannels(ctx request.CTX, job *model.Job, writer io.Writer, teamNames map[string]bool, withArchived bool) *model.AppError {
|
||||
afterId := strings.Repeat("0", 26)
|
||||
cnt := 0
|
||||
for {
|
||||
@@ -239,7 +239,7 @@ func (a *App) exportAllChannels(ctx request.CTX, job *model.Job, writer io.Write
|
||||
afterId = channel.Id
|
||||
|
||||
// Skip deleted.
|
||||
if channel.DeleteAt != 0 {
|
||||
if channel.DeleteAt != 0 && !withArchived {
|
||||
continue
|
||||
}
|
||||
// Skip channels on deleted teams.
|
||||
@@ -257,7 +257,7 @@ func (a *App) exportAllChannels(ctx request.CTX, job *model.Job, writer io.Write
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) exportAllUsers(ctx request.CTX, job *model.Job, writer io.Writer) *model.AppError {
|
||||
func (a *App) exportAllUsers(ctx request.CTX, job *model.Job, writer io.Writer, includeArchivedChannels bool) *model.AppError {
|
||||
afterId := strings.Repeat("0", 26)
|
||||
cnt := 0
|
||||
for {
|
||||
@@ -319,7 +319,7 @@ func (a *App) exportAllUsers(ctx request.CTX, job *model.Job, writer io.Writer)
|
||||
userLine.User.NotifyProps = a.buildUserNotifyProps(user.NotifyProps)
|
||||
|
||||
// Do the Team Memberships.
|
||||
members, err := a.buildUserTeamAndChannelMemberships(user.Id)
|
||||
members, err := a.buildUserTeamAndChannelMemberships(user.Id, includeArchivedChannels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -335,7 +335,7 @@ func (a *App) exportAllUsers(ctx request.CTX, job *model.Job, writer io.Writer)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) buildUserTeamAndChannelMemberships(userID string) (*[]imports.UserTeamImportData, *model.AppError) {
|
||||
func (a *App) buildUserTeamAndChannelMemberships(userID string, includeArchivedChannels bool) (*[]imports.UserTeamImportData, *model.AppError) {
|
||||
var memberships []imports.UserTeamImportData
|
||||
|
||||
members, err := a.Srv().Store().Team().GetTeamMembersForExport(userID)
|
||||
@@ -353,7 +353,7 @@ func (a *App) buildUserTeamAndChannelMemberships(userID string) (*[]imports.User
|
||||
memberData := ImportUserTeamDataFromTeamMember(member)
|
||||
|
||||
// Do the Channel Memberships.
|
||||
channelMembers, err := a.buildUserChannelMemberships(userID, member.TeamId)
|
||||
channelMembers, err := a.buildUserChannelMemberships(userID, member.TeamId, includeArchivedChannels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -372,8 +372,8 @@ func (a *App) buildUserTeamAndChannelMemberships(userID string) (*[]imports.User
|
||||
return &memberships, nil
|
||||
}
|
||||
|
||||
func (a *App) buildUserChannelMemberships(userID string, teamID string) (*[]imports.UserChannelImportData, *model.AppError) {
|
||||
members, nErr := a.Srv().Store().Channel().GetChannelMembersForExport(userID, teamID)
|
||||
func (a *App) buildUserChannelMemberships(userID string, teamID string, includeArchivedChannels bool) (*[]imports.UserChannelImportData, *model.AppError) {
|
||||
members, nErr := a.Srv().Store().Channel().GetChannelMembersForExport(userID, teamID, includeArchivedChannels)
|
||||
if nErr != nil {
|
||||
return nil, model.NewAppError("buildUserChannelMemberships", "app.channel.get_members.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
||||
}
|
||||
@@ -411,7 +411,7 @@ func (a *App) buildUserNotifyProps(notifyProps model.StringMap) *imports.UserNot
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) exportAllPosts(ctx request.CTX, job *model.Job, writer io.Writer, withAttachments bool) ([]imports.AttachmentImportData, *model.AppError) {
|
||||
func (a *App) exportAllPosts(ctx request.CTX, job *model.Job, writer io.Writer, withAttachments bool, includeArchivedChannels bool) ([]imports.AttachmentImportData, *model.AppError) {
|
||||
var attachments []imports.AttachmentImportData
|
||||
afterId := strings.Repeat("0", 26)
|
||||
var postProcessCount uint64
|
||||
@@ -424,7 +424,7 @@ func (a *App) exportAllPosts(ctx request.CTX, job *model.Job, writer io.Writer,
|
||||
logCheckpoint = time.Now()
|
||||
}
|
||||
|
||||
posts, nErr := a.Srv().Store().Post().GetParentsForExportAfter(1000, afterId)
|
||||
posts, nErr := a.Srv().Store().Post().GetParentsForExportAfter(1000, afterId, includeArchivedChannels)
|
||||
if nErr != nil {
|
||||
return nil, model.NewAppError("exportAllPosts", "app.post.get_posts.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ func ImportLineFromChannel(channel *model.ChannelForExport) *imports.LineImportD
|
||||
Header: &channel.Header,
|
||||
Purpose: &channel.Purpose,
|
||||
Scheme: channel.SchemeName,
|
||||
DeletedAt: &channel.DeleteAt,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ func TestExportUserChannels(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
th.App.UpdateChannelMemberNotifyProps(th.Context, notifyProps, channel.Id, user.Id)
|
||||
exportData, appErr := th.App.buildUserChannelMemberships(user.Id, team.Id)
|
||||
exportData, appErr := th.App.buildUserChannelMemberships(user.Id, team.Id, false)
|
||||
require.Nil(t, appErr)
|
||||
assert.Equal(t, len(*exportData), 3)
|
||||
for _, data := range *exportData {
|
||||
@@ -767,3 +767,38 @@ func TestExportDeletedTeams(t *testing.T) {
|
||||
assert.NotContains(t, team.Id, team1.Id)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExportArchivedChannels(t *testing.T) {
|
||||
th1 := Setup(t).InitBasic()
|
||||
defer th1.TearDown()
|
||||
|
||||
archivedChannel := th1.CreateChannel(th1.Context, th1.BasicTeam)
|
||||
th1.CreatePost(archivedChannel)
|
||||
appErr := th1.App.DeleteChannel(th1.Context, archivedChannel, th1.SystemAdminUser.Id)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
var b bytes.Buffer
|
||||
appErr = th1.App.BulkExport(th1.Context, &b, "somePath", nil, model.BulkExportOpts{
|
||||
IncludeArchivedChannels: true,
|
||||
})
|
||||
require.Nil(t, appErr)
|
||||
|
||||
th2 := Setup(t)
|
||||
defer th2.TearDown()
|
||||
err, i := th2.App.BulkImport(th2.Context, &b, nil, false, 5)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 0, i)
|
||||
|
||||
channels2, err := th2.App.GetAllChannels(th1.Context, 0, 10, model.ChannelSearchOpts{
|
||||
IncludeDeleted: true,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
found := false
|
||||
for i := range channels2 {
|
||||
if channels2[i].Name == archivedChannel.Name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
require.True(t, found, "archived channel not found after import")
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@ func (a *App) importChannel(c request.CTX, data *imports.ChannelImportData, dryR
|
||||
}
|
||||
|
||||
var channel *model.Channel
|
||||
if result, err := a.Srv().Store().Channel().GetByNameIncludeDeleted(team.Id, *data.Name, true); err == nil {
|
||||
if result, gErr := a.Srv().Store().Channel().GetByNameIncludeDeleted(team.Id, *data.Name, true); gErr == nil {
|
||||
channel = result
|
||||
} else {
|
||||
channel = &model.Channel{}
|
||||
@@ -311,13 +311,20 @@ func (a *App) importChannel(c request.CTX, data *imports.ChannelImportData, dryR
|
||||
channel.SchemeId = &scheme.Id
|
||||
}
|
||||
|
||||
var chErr *model.AppError
|
||||
if channel.Id == "" {
|
||||
if _, err := a.CreateChannel(c, channel, false); err != nil {
|
||||
return err
|
||||
if _, chErr = a.CreateChannel(c, channel, false); chErr != nil {
|
||||
return chErr
|
||||
}
|
||||
} else {
|
||||
if _, err := a.UpdateChannel(c, channel); err != nil {
|
||||
return err
|
||||
if _, chErr = a.UpdateChannel(c, channel); chErr != nil {
|
||||
return chErr
|
||||
}
|
||||
}
|
||||
|
||||
if data.DeletedAt != nil && *data.DeletedAt > 0 {
|
||||
if err := a.Srv().Store().Channel().Delete(channel.Id, *data.DeletedAt); err != nil {
|
||||
return model.NewAppError("BulkImport", "app.import.import_channel.deleting.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1374,7 +1381,7 @@ func (a *App) getTeamsByNames(names []string) (map[string]*model.Team, *model.Ap
|
||||
}
|
||||
|
||||
func (a *App) getChannelsByNames(names []string, teamID string) (map[string]*model.Channel, *model.AppError) {
|
||||
allChannels, err := a.Srv().Store().Channel().GetByNames(teamID, names, true)
|
||||
allChannels, err := a.Srv().Store().Channel().GetByNamesIncludeDeleted(teamID, names, true)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("BulkImport", "app.import.get_teams_by_names.some_teams_not_found.error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
|
||||
@@ -704,6 +704,21 @@ func TestImportImportChannel(t *testing.T) {
|
||||
assert.Equal(t, *data.Header, channel.Header)
|
||||
assert.Equal(t, *data.Purpose, channel.Purpose)
|
||||
assert.Equal(t, scheme2.Id, *channel.SchemeId)
|
||||
|
||||
// Do a valid archived channel.
|
||||
now := model.GetMillis()
|
||||
data.Name = ptrStr("archivedchannel")
|
||||
data.DisplayName = ptrStr("Archived Channel")
|
||||
data.Type = &chanOpen
|
||||
data.Header = ptrStr("Archived Channel Header")
|
||||
data.Purpose = ptrStr("Archived Channel Purpose")
|
||||
data.Scheme = &scheme1.Name
|
||||
data.DeletedAt = &now
|
||||
err = th.App.importChannel(th.Context, &data, false)
|
||||
require.Nil(t, err, "Expected success in apply mode")
|
||||
aChan, err := th.App.GetChannelByName(th.Context, *data.Name, team.Id, true)
|
||||
require.Nil(t, err, "Failed to get channel from database.")
|
||||
assert.Equal(t, *data.Name, aChan.Name)
|
||||
}
|
||||
|
||||
func TestImportImportUser(t *testing.T) {
|
||||
|
||||
@@ -50,6 +50,7 @@ type ChannelImportData struct {
|
||||
Header *string `json:"header,omitempty"`
|
||||
Purpose *string `json:"purpose,omitempty"`
|
||||
Scheme *string `json:"scheme,omitempty"`
|
||||
DeletedAt *int64 `json:"deleted_at,omitempty"`
|
||||
}
|
||||
|
||||
type UserImportData struct {
|
||||
|
||||
Ссылка в новой задаче
Block a user