1
0
mirror of https://github.com/mattermost/focalboard.git synced 2024-12-24 13:43:12 +02:00

Export Import Memberships is retained (#4422)

Fixes https://github.com/mattermost/focalboard/issues/4275
This commit is contained in:
Rajat Dabade 2023-01-19 08:52:14 +05:30 committed by GitHub
parent 333b448705
commit 8323b58ae0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 134 additions and 13 deletions

View File

@ -92,14 +92,25 @@ func (a *App) writeArchiveBoard(zw *zip.Writer, board model.Board, opt model.Exp
return err
}
if block.Type == model.TypeImage {
filename, err := extractImageFilename(block)
if err != nil {
filename, err2 := extractImageFilename(block)
if err2 != nil {
return err
}
files = append(files, filename)
}
}
boardMembers, err := a.GetMembersForBoard(board.ID)
if err != nil {
return err
}
for _, boardMember := range boardMembers {
if err = a.writeArchiveBoardMemberLine(w, boardMember); err != nil {
return err
}
}
// write the files
for _, filename := range files {
if err := a.writeArchiveFile(zw, filename, board.ID, opt); err != nil {
@ -109,6 +120,31 @@ func (a *App) writeArchiveBoard(zw *zip.Writer, board model.Board, opt model.Exp
return nil
}
// writeArchiveBoardMemberLine writes a single boardMember to the archive.
func (a *App) writeArchiveBoardMemberLine(w io.Writer, boardMember *model.BoardMember) error {
bm, err := json.Marshal(&boardMember)
if err != nil {
return err
}
line := model.ArchiveLine{
Type: "boardMember",
Data: bm,
}
bm, err = json.Marshal(&line)
if err != nil {
return err
}
_, err = w.Write(bm)
if err != nil {
return err
}
_, err = w.Write(newline)
return err
}
// writeArchiveBlockLine writes a single block to the archive.
func (a *App) writeArchiveBlockLine(w io.Writer, block *model.Block) error {
b, err := json.Marshal(&block)

View File

@ -137,6 +137,7 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str
}
now := utils.GetMillis()
var boardID string
var boardMembers []*model.BoardMember
lineNum := 1
firstLine := true
@ -196,6 +197,12 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str
block.UpdateAt = now
block.BoardID = boardID
boardsAndBlocks.Blocks = append(boardsAndBlocks.Blocks, block)
case "boardMember":
var boardMember *model.BoardMember
if err2 := json.Unmarshal(archiveLine.Data, &boardMember); err2 != nil {
return "", fmt.Errorf("invalid board Member in archive line %d: %w", lineNum, err2)
}
boardMembers = append(boardMembers, boardMember)
default:
return "", model.NewErrUnsupportedArchiveLineType(lineNum, archiveLine.Type)
}
@ -212,6 +219,13 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str
lineNum++
}
// loop to remove the people how are not part of the team and system
for i := len(boardMembers) - 1; i >= 0; i-- {
if _, err := a.GetUser(boardMembers[i].UserID); err != nil {
boardMembers = append(boardMembers[:i], boardMembers[i+1:]...)
}
}
a.fixBoardsandBlocks(boardsAndBlocks, opt)
var err error
@ -225,16 +239,22 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str
return "", fmt.Errorf("error inserting archive blocks: %w", err)
}
// add user to all the new boards (if not the fake system user).
if opt.ModifiedBy != model.SystemUserID {
for _, board := range boardsAndBlocks.Boards {
boardMember := &model.BoardMember{
BoardID: board.ID,
UserID: opt.ModifiedBy,
SchemeAdmin: true,
// add users to all the new boards (if not the fake system user).
for _, board := range boardsAndBlocks.Boards {
for _, boardMember := range boardMembers {
bm := &model.BoardMember{
BoardID: board.ID,
UserID: boardMember.UserID,
Roles: boardMember.Roles,
MinimumRole: boardMember.MinimumRole,
SchemeAdmin: boardMember.SchemeAdmin,
SchemeEditor: boardMember.SchemeEditor,
SchemeCommenter: boardMember.SchemeCommenter,
SchemeViewer: boardMember.SchemeViewer,
Synthetic: boardMember.Synthetic,
}
if _, err := a.AddMemberToBoard(boardMember); err != nil {
return "", fmt.Errorf("cannot add member to board: %w", err)
if _, err2 := a.AddMemberToBoard(bm); err2 != nil {
return "", fmt.Errorf("cannot add member to board: %w", err2)
}
}
}

View File

@ -47,8 +47,6 @@ func TestApp_ImportArchive(t *testing.T) {
th.Store.EXPECT().CreateBoardsAndBlocks(gomock.AssignableToTypeOf(&model.BoardsAndBlocks{}), "user").Return(babs, nil)
th.Store.EXPECT().GetMembersForBoard(board.ID).AnyTimes().Return([]*model.BoardMember{boardMember}, nil)
th.Store.EXPECT().GetBoard(board.ID).Return(board, nil)
th.Store.EXPECT().GetMemberForBoard(board.ID, "user").Return(boardMember, nil)
th.Store.EXPECT().GetUserCategoryBoards("user", "test-team")
th.Store.EXPECT().CreateCategory(utils.Anything).Return(nil)
th.Store.EXPECT().GetCategory(utils.Anything).Return(&model.Category{
@ -62,6 +60,64 @@ func TestApp_ImportArchive(t *testing.T) {
err := th.App.ImportArchive(r, opts)
require.NoError(t, err, "import archive should not fail")
})
t.Run("import board archive", func(t *testing.T) {
r := bytes.NewReader([]byte(boardArchive))
opts := model.ImportArchiveOptions{
TeamID: "test-team",
ModifiedBy: "f1tydgc697fcbp8ampr6881jea",
}
bm1 := &model.BoardMember{
BoardID: board.ID,
UserID: "f1tydgc697fcbp8ampr6881jea",
}
bm2 := &model.BoardMember{
BoardID: board.ID,
UserID: "hxxzooc3ff8cubsgtcmpn8733e",
}
bm3 := &model.BoardMember{
BoardID: board.ID,
UserID: "nto73edn5ir6ifimo5a53y1dwa",
}
user1 := &model.User{
ID: "f1tydgc697fcbp8ampr6881jea",
}
user2 := &model.User{
ID: "hxxzooc3ff8cubsgtcmpn8733e",
}
user3 := &model.User{
ID: "nto73edn5ir6ifimo5a53y1dwa",
}
th.Store.EXPECT().CreateBoardsAndBlocks(gomock.AssignableToTypeOf(&model.BoardsAndBlocks{}), "f1tydgc697fcbp8ampr6881jea").Return(babs, nil)
th.Store.EXPECT().GetMembersForBoard(board.ID).AnyTimes().Return([]*model.BoardMember{bm1, bm2, bm3}, nil)
th.Store.EXPECT().GetUserCategoryBoards("f1tydgc697fcbp8ampr6881jea", "test-team")
th.Store.EXPECT().CreateCategory(utils.Anything).Return(nil)
th.Store.EXPECT().GetCategory(utils.Anything).Return(&model.Category{
ID: "boards_category_id",
Name: "Boards",
}, nil)
th.Store.EXPECT().GetMembersForUser("f1tydgc697fcbp8ampr6881jea").Return([]*model.BoardMember{}, nil)
th.Store.EXPECT().GetBoardsForUserAndTeam("f1tydgc697fcbp8ampr6881jea", "test-team", false).Return([]*model.Board{}, nil)
th.Store.EXPECT().AddUpdateCategoryBoard("f1tydgc697fcbp8ampr6881jea", utils.Anything).Return(nil)
th.Store.EXPECT().GetBoard(board.ID).AnyTimes().Return(board, nil)
th.Store.EXPECT().GetMemberForBoard(board.ID, "f1tydgc697fcbp8ampr6881jea").AnyTimes().Return(bm1, nil)
th.Store.EXPECT().GetMemberForBoard(board.ID, "hxxzooc3ff8cubsgtcmpn8733e").AnyTimes().Return(bm2, nil)
th.Store.EXPECT().GetMemberForBoard(board.ID, "nto73edn5ir6ifimo5a53y1dwa").AnyTimes().Return(bm3, nil)
th.Store.EXPECT().GetUserByID("f1tydgc697fcbp8ampr6881jea").AnyTimes().Return(user1, nil)
th.Store.EXPECT().GetUserByID("hxxzooc3ff8cubsgtcmpn8733e").AnyTimes().Return(user2, nil)
th.Store.EXPECT().GetUserByID("nto73edn5ir6ifimo5a53y1dwa").AnyTimes().Return(user3, nil)
boardID, err := th.App.ImportBoardJSONL(r, opts)
require.Equal(t, board.ID, boardID, "Board ID should be same")
require.NoError(t, err, "import archive should not fail")
})
}
//nolint:lll
@ -78,3 +134,12 @@ const asana = `{"version":1,"date":1614714686842}
{"type":"block","data":{"id":"db1dd596-0999-4741-8b05-72ca8e438e31","fields":{"icon":"","properties":{"3bdcbaeb-bc78-4884-8531-a0323b74676a":"deaab476-c690-48df-828f-725b064dc476"},"contentOrder":[]},"createAt":1614714686841,"updateAt":1614714686841,"deleteAt":0,"schema":1,"parentId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","rootId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","modifiedBy":"","type":"card","title":"[EXAMPLE TASK] Approve campaign copy"}}
{"type":"block","data":{"id":"16861c05-f31f-46af-8429-80a87b5aa93a","fields":{"icon":"","properties":{"3bdcbaeb-bc78-4884-8531-a0323b74676a":"2138305a-3157-461c-8bbe-f19ebb55846d"},"contentOrder":[]},"createAt":1614714686841,"updateAt":1614714686841,"deleteAt":0,"schema":1,"parentId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","rootId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","modifiedBy":"","type":"card","title":"[EXAMPLE TASK] Send out updated attendee list"}}
`
//nolint:lll
const boardArchive = `{"type":"board","data":{"id":"bfoi6yy6pa3yzika53spj7pq9ee","teamId":"wsmqbtwb5jb35jb3mtp85c8a9h","channelId":"","createdBy":"nto73edn5ir6ifimo5a53y1dwa","modifiedBy":"nto73edn5ir6ifimo5a53y1dwa","type":"P","minimumRole":"","title":"Custom","description":"","icon":"","showDescription":false,"isTemplate":false,"templateVersion":0,"properties":{},"cardProperties":[{"id":"aonihehbifijmx56aqzu3cc7w1r","name":"Status","options":[],"type":"select"},{"id":"aohjkzt769rxhtcz1o9xcoce5to","name":"Person","options":[],"type":"person"}],"createAt":1672750481591,"updateAt":1672750481591,"deleteAt":0}}
{"type":"block","data":{"id":"ckpc3b1dp3pbw7bqntfryy9jbzo","parentId":"bjaqxtbyqz3bu7pgyddpgpms74a","createdBy":"nto73edn5ir6ifimo5a53y1dwa","modifiedBy":"nto73edn5ir6ifimo5a53y1dwa","schema":1,"type":"card","title":"Test","fields":{"contentOrder":[],"icon":"","isTemplate":false,"properties":{"aohjkzt769rxhtcz1o9xcoce5to":"hxxzooc3ff8cubsgtcmpn8733e"}},"createAt":1672750481612,"updateAt":1672845003530,"deleteAt":0,"boardId":"bfoi6yy6pa3yzika53spj7pq9ee"}}
{"type":"block","data":{"id":"v7tdajwpm47r3u8duedk89bhxar","parentId":"bpypang3a3errqstj1agx9kuqay","createdBy":"nto73edn5ir6ifimo5a53y1dwa","modifiedBy":"nto73edn5ir6ifimo5a53y1dwa","schema":1,"type":"view","title":"Board view","fields":{"cardOrder":["crsyw7tbr3pnjznok6ppngmmyya","c5titiemp4pgaxbs4jksgybbj4y"],"collapsedOptionIds":[],"columnCalculations":{},"columnWidths":{},"defaultTemplateId":"","filter":{"filters":[],"operation":"and"},"hiddenOptionIds":[],"kanbanCalculations":{},"sortOptions":[],"viewType":"board","visibleOptionIds":[],"visiblePropertyIds":["aohjkzt769rxhtcz1o9xcoce5to"]},"createAt":1672750481626,"updateAt":1672750481626,"deleteAt":0,"boardId":"bfoi6yy6pa3yzika53spj7pq9ee"}}
{"type":"boardMember","data":{"boardId":"bfoi6yy6pa3yzika53spj7pq9ee","userId":"f1tydgc697fcbp8ampr6881jea","roles":"","minimumRole":"","schemeAdmin":false,"schemeEditor":false,"schemeCommenter":false,"schemeViewer":true,"synthetic":false}}
{"type":"boardMember","data":{"boardId":"bfoi6yy6pa3yzika53spj7pq9ee","userId":"hxxzooc3ff8cubsgtcmpn8733e","roles":"","minimumRole":"","schemeAdmin":false,"schemeEditor":false,"schemeCommenter":false,"schemeViewer":true,"synthetic":false}}
{"type":"boardMember","data":{"boardId":"bfoi6yy6pa3yzika53spj7pq9ee","userId":"nto73edn5ir6ifimo5a53y1dwa","roles":"","minimumRole":"","schemeAdmin":true,"schemeEditor":false,"schemeCommenter":false,"schemeViewer":false,"synthetic":false}}
`