mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
[MM-12667] Allow including deactivated users in bulk import and export (#10353)
* [MM-12667] Allow including deactivated users in bulk import and export 1. Added `deleteAt` for user type import line 2. Adding deactivated users data in bulk export 3. Importing deactivated users data in bulk import Added/Updated relevant test cases * Fixed export of replies to posts by deleted users. Updated tests for same
This commit is contained in:
committed by
George Goldberg
parent
7ac5715a02
commit
7f9e1273d7
@@ -184,11 +184,6 @@ func (a *App) ExportAllUsers(writer io.Writer) *model.AppError {
|
||||
for _, user := range users {
|
||||
afterId = user.Id
|
||||
|
||||
// Skip deleted.
|
||||
if user.DeleteAt != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Gathering here the exportable preferences to pass them on to ImportLineFromUser
|
||||
exportedPrefs := make(map[string]*string)
|
||||
allPrefs, err := a.GetPreferencesForUser(user.Id)
|
||||
|
||||
@@ -68,6 +68,7 @@ func ImportLineFromUser(user *model.User, exportedPrefs map[string]*string) *Lin
|
||||
ChannelDisplayMode: exportedPrefs["ChannelDisplayMode"],
|
||||
TutorialStep: exportedPrefs["TutorialStep"],
|
||||
EmailInterval: exportedPrefs["EmailInterval"],
|
||||
DeleteAt: &user.DeleteAt,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,8 +170,13 @@ func TestExportAllUsers(t *testing.T) {
|
||||
th1 := Setup(t).InitBasic()
|
||||
defer th1.TearDown()
|
||||
|
||||
// Adding a user and deactivating it to check whether it gets included in bulk export
|
||||
user := th1.CreateUser()
|
||||
_, err := th1.App.UpdateActive(user, false)
|
||||
require.Nil(t, err)
|
||||
|
||||
var b bytes.Buffer
|
||||
err := th1.App.BulkExport(&b, "somefile", "somePath", "someDir")
|
||||
err = th1.App.BulkExport(&b, "somefile", "somePath", "someDir")
|
||||
require.Nil(t, err)
|
||||
|
||||
th2 := Setup(t)
|
||||
@@ -192,4 +197,20 @@ func TestExportAllUsers(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(users1), len(users2))
|
||||
assert.ElementsMatch(t, users1, users2)
|
||||
|
||||
// Checking whether deactivated users were included in bulk export
|
||||
deletedUsers1, err := th1.App.GetUsers(&model.UserGetOptions{
|
||||
Inactive: true,
|
||||
Page: 0,
|
||||
PerPage: 10,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
deletedUsers2, err := th1.App.GetUsers(&model.UserGetOptions{
|
||||
Inactive: true,
|
||||
Page: 0,
|
||||
PerPage: 10,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(deletedUsers1), len(deletedUsers2))
|
||||
assert.ElementsMatch(t, deletedUsers1, deletedUsers2)
|
||||
}
|
||||
|
||||
@@ -369,6 +369,13 @@ func (a *App) ImportUser(data *UserImportData, dryRun bool) *model.AppError {
|
||||
}
|
||||
}
|
||||
|
||||
if data.DeleteAt != nil {
|
||||
if user.DeleteAt != *data.DeleteAt {
|
||||
user.DeleteAt = *data.DeleteAt
|
||||
hasUserChanged = true
|
||||
}
|
||||
}
|
||||
|
||||
var roles string
|
||||
if data.Roles != nil {
|
||||
if user.Roles != *data.Roles {
|
||||
|
||||
@@ -1300,6 +1300,51 @@ func TestImportImportUser(t *testing.T) {
|
||||
assert.True(t, channelMember.SchemeUser)
|
||||
assert.Equal(t, "", channelMember.ExplicitRoles)
|
||||
|
||||
|
||||
// Test importing deleted user with a valid team & valid channel name in apply mode.
|
||||
username = model.NewId()
|
||||
deleteAt := model.GetMillis()
|
||||
deletedUserData := &UserImportData{
|
||||
Username: &username,
|
||||
DeleteAt: &deleteAt,
|
||||
Email: ptrStr(model.NewId() + "@example.com"),
|
||||
Teams: &[]UserTeamImportData{
|
||||
{
|
||||
Name: &team.Name,
|
||||
Roles: ptrStr("team_user"),
|
||||
Channels: &[]UserChannelImportData{
|
||||
{
|
||||
Name: &channel.Name,
|
||||
Roles: ptrStr("channel_user"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
err = th.App.ImportUser(deletedUserData, false)
|
||||
assert.Nil(t, err)
|
||||
|
||||
user, err = th.App.GetUserByUsername(*deletedUserData.Username)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get user from database.")
|
||||
}
|
||||
|
||||
teamMember, err = th.App.GetTeamMember(team.Id, user.Id)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get the team member")
|
||||
}
|
||||
|
||||
assert.True(t, teamMember.SchemeUser)
|
||||
assert.Equal(t, "", teamMember.ExplicitRoles)
|
||||
|
||||
channelMember, err = th.App.GetChannelMember(channel.Id, user.Id)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get the channel member")
|
||||
}
|
||||
|
||||
assert.True(t, channelMember.SchemeUser)
|
||||
assert.Equal(t, "", channelMember.ExplicitRoles)
|
||||
|
||||
}
|
||||
|
||||
func TestImportUserDefaultNotifyProps(t *testing.T) {
|
||||
|
||||
@@ -182,12 +182,13 @@ func TestImportBulkImport(t *testing.T) {
|
||||
{"type": "channel", "channel": {"type": "O", "display_name": "xr6m6udffngark2uekvr3hoeny", "team": "` + teamName + `", "name": "` + channelName + `"}}
|
||||
{"type": "user", "user": {"username": "` + username + `", "email": "` + username + `@example.com", "teams": [{"name": "` + teamName + `","theme": "` + teamTheme1 + `", "channels": [{"name": "` + channelName + `"}]}]}}
|
||||
{"type": "user", "user": {"username": "` + username2 + `", "email": "` + username2 + `@example.com", "teams": [{"name": "` + teamName + `","theme": "` + teamTheme2 + `", "channels": [{"name": "` + channelName + `"}]}]}}
|
||||
{"type": "user", "user": {"username": "` + username3 + `", "email": "` + username3 + `@example.com", "teams": [{"name": "` + teamName + `", "channels": [{"name": "` + channelName + `"}]}]}}
|
||||
{"type": "user", "user": {"username": "` + username3 + `", "email": "` + username3 + `@example.com", "teams": [{"name": "` + teamName + `", "channels": [{"name": "` + channelName + `"}], "delete_at": 123456789016}]}}
|
||||
{"type": "post", "post": {"team": "` + teamName + `", "channel": "` + channelName + `", "user": "` + username + `", "message": "Hello World", "create_at": 123456789012, "attachments":[{"path": "` + testImage + `"}]}}
|
||||
{"type": "post", "post": {"team": "` + teamName + `", "channel": "` + channelName + `", "user": "` + username3 + `", "message": "Hey Everyone!", "create_at": 123456789013, "attachments":[{"path": "` + testImage + `"}]}}
|
||||
{"type": "direct_channel", "direct_channel": {"members": ["` + username + `", "` + username2 + `"]}}
|
||||
{"type": "direct_channel", "direct_channel": {"members": ["` + username + `", "` + username2 + `", "` + username3 + `"]}}
|
||||
{"type": "direct_post", "direct_post": {"channel_members": ["` + username + `", "` + username2 + `"], "user": "` + username + `", "message": "Hello Direct Channel", "create_at": 123456789013}}
|
||||
{"type": "direct_post", "direct_post": {"channel_members": ["` + username + `", "` + username2 + `", "` + username3 + `"], "user": "` + username + `", "message": "Hello Group Channel", "create_at": 123456789014}}
|
||||
{"type": "direct_post", "direct_post": {"channel_members": ["` + username + `", "` + username2 + `"], "user": "` + username + `", "message": "Hello Direct Channel", "create_at": 123456789014}}
|
||||
{"type": "direct_post", "direct_post": {"channel_members": ["` + username + `", "` + username2 + `", "` + username3 + `"], "user": "` + username + `", "message": "Hello Group Channel", "create_at": 123456789015}}
|
||||
{"type": "emoji", "emoji": {"name": "` + emojiName + `", "image": "` + testImage + `"}}`
|
||||
|
||||
if err, line := th.App.BulkImport(strings.NewReader(data1), false, 2); err != nil || line != 0 {
|
||||
|
||||
@@ -55,6 +55,7 @@ type UserImportData struct {
|
||||
UseMarkdownPreview *string `json:"feature_enabled_markdown_preview,omitempty"`
|
||||
UseFormatting *string `json:"formatting,omitempty"`
|
||||
ShowUnreadSection *string `json:"show_unread_section,omitempty"`
|
||||
DeleteAt *int64 `json:"delete_at,omitempty"`
|
||||
|
||||
Teams *[]UserTeamImportData `json:"teams,omitempty"`
|
||||
|
||||
|
||||
@@ -1327,7 +1327,6 @@ func (s *SqlPostStore) GetParentsForExportAfter(limit int, afterId string) store
|
||||
AND p1.DeleteAt = 0
|
||||
AND Channels.DeleteAt = 0
|
||||
AND Teams.DeleteAt = 0
|
||||
AND Users.DeleteAt = 0
|
||||
ORDER BY
|
||||
p1.Id
|
||||
LIMIT
|
||||
@@ -1356,7 +1355,6 @@ func (s *SqlPostStore) GetRepliesForExport(parentId string) store.StoreChannel {
|
||||
WHERE
|
||||
Posts.ParentId = :ParentId
|
||||
AND Posts.DeleteAt = 0
|
||||
AND Users.DeleteAt = 0
|
||||
ORDER BY
|
||||
Posts.Id`,
|
||||
map[string]interface{}{"ParentId": parentId})
|
||||
|
||||
@@ -1941,4 +1941,20 @@ func testPostStoreGetRepliesForExport(t *testing.T, ss store.Store) {
|
||||
assert.Equal(t, reply1.Id, p2.Id)
|
||||
assert.Equal(t, reply1.Message, p2.Message)
|
||||
assert.Equal(t, reply1.Username, u1.Username)
|
||||
|
||||
// Checking whether replies by deleted user are exported
|
||||
u1.DeleteAt = 1002
|
||||
store.Must(ss.User().Update(&u1, false))
|
||||
|
||||
r1 = <-ss.Post().GetRepliesForExport(p1.Id)
|
||||
assert.Nil(t, r1.Err)
|
||||
|
||||
d1 = r1.Data.([]*model.ReplyForExport)
|
||||
assert.Len(t, d1, 1)
|
||||
|
||||
reply1 = d1[0]
|
||||
assert.Equal(t, reply1.Id, p2.Id)
|
||||
assert.Equal(t, reply1.Message, p2.Message)
|
||||
assert.Equal(t, reply1.Username, u1.Username)
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user