import * as fs from 'fs' ; import { exit } from 'process' ; 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 { Card , createCard } from '../../webapp/src/blocks/card' ; import { createTextBlock } from '../../webapp/src/blocks/textBlock' ; import { Utils } from './utils' ; import xml2js , { ParserOptions } from 'xml2js' ; import TurndownService from 'turndown' ; ( global . window as any ) = { } ; const optionColors = [ 'propColorGray' , 'propColorBrown' , 'propColorOrange' , 'propColorYellow' , 'propColorGreen' , 'propColorBlue' , 'propColorPurple' , 'propColorPink' , 'propColorRed' , ] ; let optionColorIndex = 0 ; var turndownService = new TurndownService ( ) ; async function run ( inputFile , outputFile ) { console . log ( `input: ${ inputFile }` ) ; console . log ( `output: ${ outputFile }` ) ; if ( ! inputFile ) { showHelp ( ) ; } if ( ! fs . existsSync ( inputFile ) ) { console . error ( `File not found: ${ inputFile }` ) ; exit ( 2 ) ; } console . log ( `Reading ${ inputFile }` ) ; const inputData = fs . readFileSync ( inputFile , 'utf-8' ) ; if ( ! inputData ) { console . error ( `Unable to read data from file: ${ inputFile }` ) ; exit ( 2 ) ; } console . log ( `Read ${ Math . round ( inputData . length / 1024 ) } KB` ) ; const parserOptions = { explicitArray : false } ; const parser = new xml2js . Parser ( parserOptions ) ; const input = await parser . parseStringPromise ( inputData ) ; if ( ! input ?. rss ?. channel ) { console . error ( `No channels in xml: ${ inputFile }` ) ; exit ( 2 ) ; } const channel = input . rss . channel ; const items = channel . item ; const blocks = convert ( items ) ; const outputData = ArchiveUtils . buildBlockArchive ( blocks ) ; fs . writeFileSync ( outputFile , outputData ) ; console . log ( `Exported ${ blocks . length } block(s) to ${ outputFile }` ) ; return blocks . length ; } function convert ( items ) { const blocks = [ ] ; const board = createBoard ( ) ; board . rootId = board . id ; board . title = 'Jira import' ; board . fields . cardProperties = [ ] ; const priorityProperty = buildCardPropertyFromValues ( 'Priority' , items . map ( o => o . priority ?. _ ) ) ; board . fields . cardProperties . push ( priorityProperty ) ; const statusProperty = buildCardPropertyFromValues ( 'Status' , items . map ( o => o . status ?. _ ) ) ; board . fields . cardProperties . push ( statusProperty ) ; const resolutionProperty = buildCardPropertyFromValues ( 'Resolution' , items . map ( o => o . resolution ?. _ ) ) ; board . fields . cardProperties . push ( resolutionProperty ) ; const typeProperty = buildCardPropertyFromValues ( 'Type' , items . map ( o => o . type ?. _ ) ) ; board . fields . cardProperties . push ( typeProperty ) ; const assigneeProperty = buildCardPropertyFromValues ( 'Assignee' , items . map ( o => o . assignee ?. _ ) ) ; board . fields . cardProperties . push ( assigneeProperty ) ; const reporterProperty = buildCardPropertyFromValues ( 'Reporter' , items . map ( o => o . reporter ?. _ ) ) ; board . fields . cardProperties . push ( reporterProperty ) ; const originalUrlProperty = { id : Utils . createGuid ( ) , name : 'Original URL' , type : 'url' , options : [ ] } ; board . fields . cardProperties . push ( originalUrlProperty ) ; const createdDateProperty = { id : Utils . createGuid ( ) , name : 'Created Date' , type : 'date' , options : [ ] } ; board . fields . cardProperties . push ( createdDateProperty ) ; blocks . push ( board ) ; const view = createBoardView ( ) ; view . title = 'Board View' ; view . fields . viewType = 'board' ; view . rootId = board . id ; view . parentId = board . id ; blocks . push ( view ) ; for ( const item of items ) { console . log ( `Item: ${ item . summary }, ` + `priority: ${ item . priority ?. _ }, ` + `status: ${ item . status ?. _ }, ` + `type: ${ item . type ?. _ }` ) ; const card = createCard ( ) ; card . title = item . summary ; card . rootId = board . id ; card . parentId = board . id ; if ( item . priority ?. _ ) { setSelectProperty ( card , priorityProperty , item . priority . _ ) ; } if ( item . status ?. _ ) { setSelectProperty ( card , statusProperty , item . status . _ ) ; } if ( item . resolution ?. _ ) { setSelectProperty ( card , resolutionProperty , item . resolution . _ ) ; } if ( item . type ?. _ ) { setSelectProperty ( card , typeProperty , item . type . _ ) ; } if ( item . assignee ?. _ ) { setSelectProperty ( card , assigneeProperty , item . assignee . _ ) ; } if ( item . reporter ?. _ ) { setSelectProperty ( card , reporterProperty , item . reporter . _ ) ; } if ( item . link ) { setProperty ( card , originalUrlProperty . id , item . link ) ; } if ( item . created ) { const dateInMs = Date . parse ( item . created ) ; setProperty ( card , createdDateProperty . id , dateInMs . toString ( ) ) ; } if ( item . description ) { const description = turndownService . turndown ( item . description ) ; console . log ( `\t${ description }` ) ; const text = createTextBlock ( ) ; text . title = description ; text . rootId = board . id ; text . parentId = card . id ; blocks . push ( text ) ; card . fields . contentOrder = [ text . id ] ; } blocks . push ( card ) ; } return blocks ; } function buildCardPropertyFromValues ( propertyName , allValues ) { const options = [ ] ; const values = allValues . filter ( o => ! ! o ) . filter ( ( x , y ) => allValues . indexOf ( x ) == y ) ; for ( const value of values ) { const optionId = Utils . createGuid ( ) ; const color = optionColors [ optionColorIndex % optionColors . length ] ; optionColorIndex += 1 ; const option = { id : optionId , value , color , } ; options . push ( option ) ; } const cardProperty = { id : Utils . createGuid ( ) , name : propertyName , type : 'select' , options } ; console . log ( `Property: ${ propertyName }, values: ${ values }` ) ; return cardProperty ; } function setSelectProperty ( card , cardProperty , propertyValue ) { const option = optionForPropertyValue ( cardProperty , propertyValue ) ; if ( option ) { card . fields . properties [ cardProperty . id ] = option . id ; } } function setProperty ( card , cardPropertyId , propertyValue ) { card . fields . properties [ cardPropertyId ] = propertyValue ; } function optionForPropertyValue ( cardProperty , propertyValue ) { const option = cardProperty . options . find ( o => o . value === propertyValue ) ; if ( ! option ) { console . error ( `Property value not found: ${ propertyValue }` ) ; return null ; } return option ; } function showHelp ( ) { console . log ( 'import -i <input.xml> -o [output.focalboard]' ) ; exit ( 1 ) ; } export { run } ;