[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
Этот коммит содержится в:
коммит произвёл
George Goldberg
родитель
7ac5715a02
Коммит
7f9e1273d7
@@ -184,11 +184,6 @@ func (a *App) ExportAllUsers(writer io.Writer) *model.AppError {
|
|||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
afterId = user.Id
|
afterId = user.Id
|
||||||
|
|
||||||
// Skip deleted.
|
|
||||||
if user.DeleteAt != 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gathering here the exportable preferences to pass them on to ImportLineFromUser
|
// Gathering here the exportable preferences to pass them on to ImportLineFromUser
|
||||||
exportedPrefs := make(map[string]*string)
|
exportedPrefs := make(map[string]*string)
|
||||||
allPrefs, err := a.GetPreferencesForUser(user.Id)
|
allPrefs, err := a.GetPreferencesForUser(user.Id)
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ func ImportLineFromUser(user *model.User, exportedPrefs map[string]*string) *Lin
|
|||||||
ChannelDisplayMode: exportedPrefs["ChannelDisplayMode"],
|
ChannelDisplayMode: exportedPrefs["ChannelDisplayMode"],
|
||||||
TutorialStep: exportedPrefs["TutorialStep"],
|
TutorialStep: exportedPrefs["TutorialStep"],
|
||||||
EmailInterval: exportedPrefs["EmailInterval"],
|
EmailInterval: exportedPrefs["EmailInterval"],
|
||||||
|
DeleteAt: &user.DeleteAt,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,8 +170,13 @@ func TestExportAllUsers(t *testing.T) {
|
|||||||
th1 := Setup(t).InitBasic()
|
th1 := Setup(t).InitBasic()
|
||||||
defer th1.TearDown()
|
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
|
var b bytes.Buffer
|
||||||
err := th1.App.BulkExport(&b, "somefile", "somePath", "someDir")
|
err = th1.App.BulkExport(&b, "somefile", "somePath", "someDir")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
th2 := Setup(t)
|
th2 := Setup(t)
|
||||||
@@ -192,4 +197,20 @@ func TestExportAllUsers(t *testing.T) {
|
|||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, len(users1), len(users2))
|
assert.Equal(t, len(users1), len(users2))
|
||||||
assert.ElementsMatch(t, users1, 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
|
var roles string
|
||||||
if data.Roles != nil {
|
if data.Roles != nil {
|
||||||
if user.Roles != *data.Roles {
|
if user.Roles != *data.Roles {
|
||||||
|
|||||||
@@ -1300,6 +1300,51 @@ func TestImportImportUser(t *testing.T) {
|
|||||||
assert.True(t, channelMember.SchemeUser)
|
assert.True(t, channelMember.SchemeUser)
|
||||||
assert.Equal(t, "", channelMember.ExplicitRoles)
|
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) {
|
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": "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": "` + 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": "` + 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": "` + 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 + `"]}}
|
||||||
{"type": "direct_channel", "direct_channel": {"members": ["` + username + `", "` + username2 + `", "` + username3 + `"]}}
|
{"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 + `"], "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": 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 + `"}}`
|
{"type": "emoji", "emoji": {"name": "` + emojiName + `", "image": "` + testImage + `"}}`
|
||||||
|
|
||||||
if err, line := th.App.BulkImport(strings.NewReader(data1), false, 2); err != nil || line != 0 {
|
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"`
|
UseMarkdownPreview *string `json:"feature_enabled_markdown_preview,omitempty"`
|
||||||
UseFormatting *string `json:"formatting,omitempty"`
|
UseFormatting *string `json:"formatting,omitempty"`
|
||||||
ShowUnreadSection *string `json:"show_unread_section,omitempty"`
|
ShowUnreadSection *string `json:"show_unread_section,omitempty"`
|
||||||
|
DeleteAt *int64 `json:"delete_at,omitempty"`
|
||||||
|
|
||||||
Teams *[]UserTeamImportData `json:"teams,omitempty"`
|
Teams *[]UserTeamImportData `json:"teams,omitempty"`
|
||||||
|
|
||||||
|
|||||||
@@ -1327,7 +1327,6 @@ func (s *SqlPostStore) GetParentsForExportAfter(limit int, afterId string) store
|
|||||||
AND p1.DeleteAt = 0
|
AND p1.DeleteAt = 0
|
||||||
AND Channels.DeleteAt = 0
|
AND Channels.DeleteAt = 0
|
||||||
AND Teams.DeleteAt = 0
|
AND Teams.DeleteAt = 0
|
||||||
AND Users.DeleteAt = 0
|
|
||||||
ORDER BY
|
ORDER BY
|
||||||
p1.Id
|
p1.Id
|
||||||
LIMIT
|
LIMIT
|
||||||
@@ -1356,7 +1355,6 @@ func (s *SqlPostStore) GetRepliesForExport(parentId string) store.StoreChannel {
|
|||||||
WHERE
|
WHERE
|
||||||
Posts.ParentId = :ParentId
|
Posts.ParentId = :ParentId
|
||||||
AND Posts.DeleteAt = 0
|
AND Posts.DeleteAt = 0
|
||||||
AND Users.DeleteAt = 0
|
|
||||||
ORDER BY
|
ORDER BY
|
||||||
Posts.Id`,
|
Posts.Id`,
|
||||||
map[string]interface{}{"ParentId": parentId})
|
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.Id, p2.Id)
|
||||||
assert.Equal(t, reply1.Message, p2.Message)
|
assert.Equal(t, reply1.Message, p2.Message)
|
||||||
assert.Equal(t, reply1.Username, u1.Username)
|
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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user