mirror of
https://github.com/mattermost/focalboard.git
synced 2024-12-15 09:14:11 +02:00
Cleanup archive import
This commit is contained in:
parent
8e1c5941bb
commit
1f461adbf8
@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import {ArchiveUtils, IArchiveHeader, IArchiveLine, IBlockArchiveLine} from './blocks/archive'
|
import {ArchiveUtils, IArchiveHeader, IArchiveLine, IBlockArchiveLine} from './blocks/archive'
|
||||||
import {IBlock, IMutableBlock} from './blocks/block'
|
import {IBlock} from './blocks/block'
|
||||||
import {LineReader} from './lineReader'
|
import {LineReader} from './lineReader'
|
||||||
import mutator from './mutator'
|
import mutator from './mutator'
|
||||||
import {Utils} from './utils'
|
import {Utils} from './utils'
|
||||||
@ -65,7 +65,9 @@ class Archiver {
|
|||||||
case 'block': {
|
case 'block': {
|
||||||
const blockLine = row as IBlockArchiveLine
|
const blockLine = row as IBlockArchiveLine
|
||||||
const block = blockLine.data
|
const block = blockLine.data
|
||||||
blocks.push(block)
|
if (Archiver.isValidBlock(block)) {
|
||||||
|
blocks.push(block)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,6 +76,14 @@ class Archiver {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static isValidBlock(block: IBlock): boolean {
|
||||||
|
if (!block.id || !block.rootId) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
static importFullArchive(onComplete?: () => void): void {
|
static importFullArchive(onComplete?: () => void): void {
|
||||||
const input = document.createElement('input')
|
const input = document.createElement('input')
|
||||||
input.type = 'file'
|
input.type = 'file'
|
||||||
@ -83,19 +93,9 @@ class Archiver {
|
|||||||
if (file) {
|
if (file) {
|
||||||
const blocks = await Archiver.readBlocksFromFile(file)
|
const blocks = await Archiver.readBlocksFromFile(file)
|
||||||
|
|
||||||
// Basic error checking
|
Utils.log(`Importing ${blocks.length} blocks...`)
|
||||||
let filteredBlocks = blocks.filter((o) => Boolean(o.id))
|
await mutator.importFullArchive(blocks)
|
||||||
|
Utils.log(`Imported ${blocks.length} blocks.`)
|
||||||
Utils.log(`Import ${filteredBlocks.length} filtered blocks with ids.`)
|
|
||||||
|
|
||||||
this.fixRootIds(filteredBlocks)
|
|
||||||
|
|
||||||
filteredBlocks = filteredBlocks.filter((o) => Boolean(o.rootId))
|
|
||||||
|
|
||||||
Utils.log(`Import ${filteredBlocks.length} filtered blocks with rootIds.`)
|
|
||||||
|
|
||||||
await mutator.importFullArchive(filteredBlocks)
|
|
||||||
Utils.log('Import completed')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onComplete?.()
|
onComplete?.()
|
||||||
@ -107,42 +107,6 @@ class Archiver {
|
|||||||
|
|
||||||
// TODO: Remove or reuse input
|
// TODO: Remove or reuse input
|
||||||
}
|
}
|
||||||
|
|
||||||
private static fixRootIds(blocks: IMutableBlock[]) {
|
|
||||||
const blockMap = new Map(blocks.map((o) => [o.id, o]))
|
|
||||||
const maxLevels = 5
|
|
||||||
for (let i = 0; i < maxLevels; i++) {
|
|
||||||
let missingRootIds = false
|
|
||||||
blocks.forEach((o) => {
|
|
||||||
if (o.parentId) {
|
|
||||||
const parent = blockMap.get(o.parentId)
|
|
||||||
if (parent) {
|
|
||||||
o.rootId = parent.rootId
|
|
||||||
} else {
|
|
||||||
Utils.assert(`No parent for ${o.type}: ${o.id} (${o.title})`)
|
|
||||||
}
|
|
||||||
if (!o.rootId) {
|
|
||||||
missingRootIds = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
o.rootId = o.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!missingRootIds) {
|
|
||||||
Utils.log(`fixRootIds in ${i} levels`)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check and log remaining errors
|
|
||||||
blocks.forEach((o) => {
|
|
||||||
if (!o.rootId) {
|
|
||||||
const parent = blockMap.get(o.parentId)
|
|
||||||
Utils.logError(`RootId is null: ${o.type} ${o.id}, parentId ${o.parentId}: ${o.title}, parent: ${parent?.type}, parent.rootId: ${parent?.rootId}, parent.title: ${parent?.title}`)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export {Archiver}
|
export {Archiver}
|
||||||
|
@ -2,17 +2,17 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
class LineReader {
|
class LineReader {
|
||||||
private static appendBuffer(buffer1: ArrayBuffer, buffer2: ArrayBuffer) {
|
private static appendBuffer(buffer1: Uint8Array, buffer2: Uint8Array): Uint8Array {
|
||||||
const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength)
|
const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength)
|
||||||
tmp.set(new Uint8Array(buffer1), 0)
|
tmp.set(buffer1, 0)
|
||||||
tmp.set(new Uint8Array(buffer2), buffer1.byteLength)
|
tmp.set(buffer2, buffer1.byteLength)
|
||||||
return tmp.buffer
|
|
||||||
|
return tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
private static arrayBufferIndexOf(buffer: ArrayBuffer, charCode: number): number {
|
private static arrayBufferIndexOf(buffer: Uint8Array, charCode: number): number {
|
||||||
const view = new Uint8Array(buffer)
|
for (let i = 0; i < buffer.byteLength; ++i) {
|
||||||
for (let i = 0; i < view.byteLength; ++i) {
|
if (buffer[i] === charCode) {
|
||||||
if (view[i] === charCode) {
|
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -21,7 +21,7 @@ class LineReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static readFile(file: File, callback: (line: string, completed: boolean) => void): void {
|
static readFile(file: File, callback: (line: string, completed: boolean) => void): void {
|
||||||
let buffer = new ArrayBuffer(0)
|
let buffer = new Uint8Array(0)
|
||||||
|
|
||||||
const chunkSize = 1024 * 1000
|
const chunkSize = 1024 * 1000
|
||||||
let offset = 0
|
let offset = 0
|
||||||
@ -29,7 +29,7 @@ class LineReader {
|
|||||||
const decoder = new TextDecoder()
|
const decoder = new TextDecoder()
|
||||||
|
|
||||||
fr.onload = () => {
|
fr.onload = () => {
|
||||||
const chunk = fr.result as ArrayBuffer
|
const chunk = new Uint8Array(fr.result as ArrayBuffer)
|
||||||
buffer = LineReader.appendBuffer(buffer, chunk)
|
buffer = LineReader.appendBuffer(buffer, chunk)
|
||||||
|
|
||||||
const newlineChar = 10 // '\n'
|
const newlineChar = 10 // '\n'
|
||||||
|
@ -138,9 +138,10 @@ class OctoClient {
|
|||||||
|
|
||||||
async importFullArchive(blocks: readonly IBlock[]): Promise<Response> {
|
async importFullArchive(blocks: readonly IBlock[]): Promise<Response> {
|
||||||
Utils.log(`importFullArchive: ${blocks.length} blocks(s)`)
|
Utils.log(`importFullArchive: ${blocks.length} blocks(s)`)
|
||||||
blocks.forEach((block) => {
|
|
||||||
Utils.log(`\t ${block.type}, ${block.id}`)
|
// blocks.forEach((block) => {
|
||||||
})
|
// Utils.log(`\t ${block.type}, ${block.id}`)
|
||||||
|
// })
|
||||||
const body = JSON.stringify(blocks)
|
const body = JSON.stringify(blocks)
|
||||||
return fetch(this.serverUrl + '/api/v1/blocks/import', {
|
return fetch(this.serverUrl + '/api/v1/blocks/import', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
Loading…
Reference in New Issue
Block a user