focalboard/import/asana/importAsana.ts

191 lines
5.2 KiB
TypeScript
Raw Normal View History

2021-02-18 00:55:59 +01:00
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
2021-02-17 21:03:45 +01:00
import * as fs from 'fs'
import minimist from 'minimist'
import {exit} from 'process'
2021-03-02 22:21:55 +01:00
import {ArchiveUtils} from '../../webapp/src/blocks/archive'
import {Block} from '../../webapp/src/blocks/block'
import {IPropertyOption, IPropertyTemplate, createBoard} from '../../webapp/src/blocks/board'
import {createBoardView} from '../../webapp/src/blocks/boardView'
import {createCard} from '../../webapp/src/blocks/card'
import {createTextBlock} from '../../webapp/src/blocks/textBlock'
2021-02-17 21:03:45 +01:00
import {Asana, Workspace} from './asana'
import {Utils} from './utils'
// HACKHACK: To allow Utils.CreateGuid to work
(global.window as any) = {}
2021-02-22 19:54:41 +01:00
const optionColors = [
// 'propColorDefault',
'propColorGray',
'propColorBrown',
'propColorOrange',
'propColorYellow',
'propColorGreen',
'propColorBlue',
'propColorPurple',
'propColorPink',
'propColorRed',
]
let optionColorIndex = 0
2021-02-17 21:03:45 +01:00
function main() {
const args: minimist.ParsedArgs = minimist(process.argv.slice(2))
const inputFile = args['i']
const outputFile = args['o'] || 'archive.focalboard'
if (!inputFile) {
showHelp()
}
2021-02-18 00:55:59 +01:00
if (!fs.existsSync(inputFile)) {
console.error(`File not found: ${inputFile}`)
exit(2)
}
2021-02-17 21:03:45 +01:00
// Read input
const inputData = fs.readFileSync(inputFile, 'utf-8')
const input = JSON.parse(inputData) as Asana
// Convert
2021-03-02 22:21:55 +01:00
const blocks = convert(input)
2021-02-17 21:03:45 +01:00
// Save output
2021-03-02 22:21:55 +01:00
// TODO: Stream output
const outputData = ArchiveUtils.buildBlockArchive(blocks)
2021-02-17 21:03:45 +01:00
fs.writeFileSync(outputFile, outputData)
console.log(`Exported to ${outputFile}`)
}
function getProjects(input: Asana): Workspace[] {
2021-02-18 00:55:59 +01:00
const projectMap = new Map<string, Workspace>()
2021-02-17 21:03:45 +01:00
2021-02-18 00:55:59 +01:00
input.data.forEach(datum => {
datum.projects.forEach(project => {
if (!projectMap.get(project.gid)) {
projectMap.set(project.gid, project)
}
})
})
2021-02-17 21:03:45 +01:00
2021-02-18 00:55:59 +01:00
return [...projectMap.values()]
2021-02-17 21:03:45 +01:00
}
function getSections(input: Asana, projectId: string): Workspace[] {
2021-02-18 00:55:59 +01:00
const sectionMap = new Map<string, Workspace>()
input.data.forEach(datum => {
const membership = datum.memberships.find(o => o.project.gid === projectId)
if (membership) {
if (!sectionMap.get(membership.section.gid)) {
sectionMap.set(membership.section.gid, membership.section)
}
}
})
return [...sectionMap.values()]
2021-02-17 21:03:45 +01:00
}
function convert(input: Asana): Block[] {
2021-02-18 00:55:59 +01:00
const projects = getProjects(input)
if (projects.length < 1) {
console.error('No projects found')
2021-03-02 22:21:55 +01:00
return []
2021-02-18 00:55:59 +01:00
}
2021-02-17 21:03:45 +01:00
2021-02-18 00:55:59 +01:00
// TODO: Handle multiple projects
const project = projects[0]
2021-02-17 21:03:45 +01:00
const blocks: Block[] = []
2021-02-17 21:03:45 +01:00
// Board
const board = createBoard()
2021-02-17 21:03:45 +01:00
console.log(`Board: ${project.name}`)
board.rootId = board.id
board.title = project.name
// Convert sections (columns) to a Select property
const optionIdMap = new Map<string, string>()
const options: IPropertyOption[] = []
2021-02-18 00:55:59 +01:00
const sections = getSections(input, project.gid)
2021-02-17 21:03:45 +01:00
sections.forEach(section => {
const optionId = Utils.createGuid()
optionIdMap.set(section.gid, optionId)
2021-02-22 19:54:41 +01:00
const color = optionColors[optionColorIndex % optionColors.length]
optionColorIndex += 1
2021-02-17 21:03:45 +01:00
const option: IPropertyOption = {
id: optionId,
value: section.name,
2021-02-22 19:54:41 +01:00
color,
2021-02-17 21:03:45 +01:00
}
options.push(option)
})
const cardProperty: IPropertyTemplate = {
id: Utils.createGuid(),
name: 'Section',
type: 'select',
options
}
board.fields.cardProperties = [cardProperty]
2021-02-17 21:03:45 +01:00
blocks.push(board)
// Board view
const view = createBoardView()
2021-02-17 21:03:45 +01:00
view.title = 'Board View'
view.fields.viewType = 'board'
2021-02-17 21:03:45 +01:00
view.rootId = board.id
view.parentId = board.id
blocks.push(view)
// Cards
input.data.forEach(card => {
console.log(`Card: ${card.name}`)
const outCard = createCard()
2021-02-17 21:03:45 +01:00
outCard.title = card.name
outCard.rootId = board.id
outCard.parentId = board.id
// Map lists to Select property options
2021-02-18 00:55:59 +01:00
const membership = card.memberships.find(o => o.project.gid === project.gid)
2021-02-17 21:03:45 +01:00
if (membership) {
const optionId = optionIdMap.get(membership.section.gid)
if (optionId) {
outCard.fields.properties[cardProperty.id] = optionId
2021-02-17 21:03:45 +01:00
} else {
console.warn(`Invalid idList: ${membership.section.gid} for card: ${card.name}`)
}
} else {
console.warn(`Missing idList for card: ${card.name}`)
}
blocks.push(outCard)
if (card.notes) {
// console.log(`\t${card.notes}`)
const text = createTextBlock()
2021-02-17 21:03:45 +01:00
text.title = card.notes
text.rootId = board.id
text.parentId = outCard.id
blocks.push(text)
outCard.fields.contentOrder = [text.id]
2021-02-17 21:03:45 +01:00
}
})
console.log('')
console.log(`Found ${input.data.length} card(s).`)
2021-03-02 22:21:55 +01:00
return blocks
2021-02-17 21:03:45 +01:00
}
function showHelp() {
console.log('import -i <input.json> -o [output.focalboard]')
2021-02-18 00:55:59 +01:00
exit(1)
2021-02-17 21:03:45 +01:00
}
main()