@@ -4,6 +4,9 @@ var Promise = require("bluebird");
4
4
var _ = require ( 'underscore' ) ;
5
5
var package = fs . readJsonSync ( 'package.json' ) ;
6
6
var Asana = require ( 'asana' ) ;
7
+ var Trello = require ( "node-trello" ) ;
8
+ var request = require ( 'request' ) ;
9
+ var path = require ( 'path' ) ;
7
10
8
11
var LABEL_COLOR = {
9
12
green : 'light-green' ,
@@ -77,9 +80,82 @@ var getUniqueName = function getUniqueName(name, haystack) {
77
80
}
78
81
} ;
79
82
83
+ var convertMap = function convertToMap ( data , map ) {
84
+ if ( _ . isArray ( data ) ) {
85
+ return _ . compact ( _ . map ( data , id => {
86
+ return convertToMap ( id , map ) ;
87
+ } ) ) ;
88
+ }
89
+
90
+ if ( typeof map [ data ] !== 'undefined' ) {
91
+ return map [ data ] ;
92
+ } else {
93
+ return null ;
94
+ }
95
+ } ;
96
+
97
+ var fetchImage = function ( url ) {
98
+ return new Promise ( function ( resolve , reject ) {
99
+ request . get ( {
100
+ url : url ,
101
+ encoding : null
102
+ } , function ( err , res , body ) {
103
+ if ( err || res . statusCode !== 200 ) {
104
+ reject ( err || res . statusCode ) ;
105
+ return ;
106
+ }
107
+
108
+ if ( body ) {
109
+ resolve ( body ) ;
110
+ } else {
111
+ resolve ( null ) ;
112
+ }
113
+ } ) ;
114
+ } ) ;
115
+ } ;
116
+
80
117
fs . readJson ( opts . config ) . then ( function ( config ) {
81
118
var client = Asana . Client . create ( ) . useAccessToken ( config . asana . personal_access_token ) ;
82
- var projects = [ ] ;
119
+ var trello = new Trello ( config . trello . key , config . trello . token ) ;
120
+ var asanaData = {
121
+ projects : [ ] ,
122
+ tags : [ ] ,
123
+ users : [ ]
124
+ } ;
125
+
126
+ var uploadImageToAsana = function ( taskId , file , filename ) {
127
+ return new Promise ( function ( resolve , reject ) {
128
+ request . post ( {
129
+ url : `https://app.asana.com/api/1.0/tasks/${ taskId } /attachments` ,
130
+ headers : {
131
+ Authorization : `Bearer ${ config . asana . personal_access_token } `
132
+ } ,
133
+ formData : {
134
+ file : {
135
+ value : file ,
136
+ options : {
137
+ filename : filename
138
+ }
139
+ }
140
+ }
141
+ } , function ( err , res , body ) {
142
+ if ( err || res . statusCode !== 200 ) {
143
+ reject ( err || res . statusCode ) ;
144
+ return ;
145
+ }
146
+
147
+ if ( body ) {
148
+ try {
149
+ body = JSON . parse ( body ) ;
150
+ } catch ( e ) { }
151
+ }
152
+
153
+ resolve ( body ) ;
154
+ } ) ;
155
+ } ) ;
156
+ } ;
157
+
158
+ Promise . promisifyAll ( trello ) ;
83
159
84
160
if ( ! config . asana . workspace ) {
85
161
console . log ( 'You should select your workspace in asana.' ) ;
@@ -107,31 +183,37 @@ fs.readJson(opts.config).then(function (config) {
107
183
} ) ;
108
184
}
109
185
110
- return client . projects . findByTeam ( config . asana . team ) . then ( fetch ) . then ( results => {
111
- projects = results ;
112
-
186
+ // Prepare asana data to avoid duplicated
187
+ return Promise . join (
188
+ client . projects . findByTeam ( config . asana . team ) . then ( fetch ) ,
189
+ client . tags . findByWorkspace ( config . asana . workspace ) . then ( fetch ) ,
190
+ client . users . findByWorkspace ( config . asana . workspace ) . then ( fetch ) ,
191
+ ( projects , tags , users ) => {
192
+ asanaData . projects = projects ;
193
+ asanaData . tags = tags ;
194
+ asanaData . users = users ;
195
+ }
196
+ ) . then ( function ( ) {
113
197
return Promise . map ( opts . files , parseJson ) ;
114
198
} ) . then ( function ( files ) {
199
+ var trellMembers = _ . flatten ( _ . pluck ( files , 'members' ) ) ;
200
+
115
201
// Check only member list
116
202
if ( opts . onlyMembers ) {
117
- var members = _ . flatten ( _ . pluck ( files , 'members' ) ) ;
118
-
119
203
console . log ( 'Trello Users' ) ;
120
- console . log ( '<username >: <FullName>' ) ;
121
- console . log ( _ . map ( members , function ( member ) {
122
- return `${ member . username } : ${ member . fullName } ` ;
204
+ console . log ( '<id >: <FullName>(<username>) ' ) ;
205
+ console . log ( _ . map ( trellMembers , function ( member ) {
206
+ return `${ member . id } : ${ member . fullName } ( ${ member . username } ) ` ;
123
207
} ) . join ( '\n' ) ) ;
124
208
125
209
console . log ( '\nAsana Users' ) ;
126
210
console . log ( '<id>: <Name>' ) ;
127
211
128
- return client . users . findByWorkspace ( config . asana . workspace ) . then ( fetch ) . then ( users => {
129
- _ . each ( users , user => {
130
- console . log ( `${ user . id } : ${ user . name } ` ) ;
131
- } ) ;
132
-
133
- throw Promise . CancellationError ;
212
+ _ . each ( asanaData . users , user => {
213
+ console . log ( `${ user . id } : ${ user . name } ` ) ;
134
214
} ) ;
215
+
216
+ throw Promise . CancellationError ;
135
217
}
136
218
137
219
// Executes in order
@@ -140,15 +222,26 @@ fs.readJson(opts.config).then(function (config) {
140
222
let listToSectionMap = { } ;
141
223
let cardToTaskMap = { } ;
142
224
let labelToTagMap = { } ;
225
+ let checklistMap = { } ;
226
+ let userMap = { } ;
227
+
228
+ _ . each ( file . checklists , checklist => {
229
+ checklistMap [ checklist . id ] = checklist ;
230
+ } ) ;
231
+
232
+ _ . each ( asanaData . users , user => {
233
+ userMap [ user . id ] = user . name ;
234
+ } ) ;
143
235
144
236
// Creates a Project
145
237
return client . projects . createInTeam ( config . asana . team , {
146
- name : getUniqueName ( file . name , _ . pluck ( projects , 'name' ) ) ,
238
+ name : getUniqueName ( file . name , _ . pluck ( asanaData . projects , 'name' ) ) ,
147
239
notes : file . desc ,
148
240
layout : 'board'
149
241
} ) . then ( result => {
150
242
console . log ( `Created ${ result . name } project in your team.` ) ;
151
243
projectData = result ;
244
+ asanaData . projects . push ( result ) ;
152
245
153
246
// Creates sections in order
154
247
return Promise . mapSeries ( file . lists , list => {
@@ -160,26 +253,124 @@ fs.readJson(opts.config).then(function (config) {
160
253
} ) ;
161
254
} ) ;
162
255
} ) . then ( ( ) => {
256
+ // Filter exists tags same with label
257
+ var labels = _ . filter ( file . labels , label => {
258
+ var matchedTag = _ . find ( asanaData . tags , tag => {
259
+ return tag . name === label . name ;
260
+ } ) ;
261
+
262
+ if ( matchedTag ) {
263
+ labelToTagMap [ label . id ] = matchedTag . id ;
264
+ return false ;
265
+ } else {
266
+ return true ;
267
+ }
268
+ } ) ;
269
+
163
270
// Creates tags
164
- return Promise . map ( file . labels , label => {
165
- return client . tags . create ( {
271
+ console . log ( `Creating ${ labels . length } tags...` ) ;
166
272
273
+ return Promise . map ( labels , label => {
274
+ return client . tags . createInWorkspace ( config . asana . workspace , {
275
+ name : label . name ,
276
+ color : LABEL_COLOR [ label . color ] ,
277
+ notes : 'Created by Trello'
167
278
} ) . then ( result => {
168
-
279
+ labelToTagMap [ label . id ] = result . id ;
280
+ asanaData . tags . push ( result ) ;
281
+ console . log ( `Created ${ result . name } (${ result . id } ) tag.` ) ;
169
282
} ) ;
170
- } ) ;
283
+ } , {
284
+ concurrency : 3
285
+ } ) . then ( function ( ) {
286
+ console . log ( `Creating ${ file . cards . length } tasks...` ) ;
287
+ let countTask = 0 ;
288
+
289
+ // Creates tasks
290
+ return Promise . mapSeries ( file . cards , card => {
291
+ return client . tasks . create ( {
292
+ assignee : card . idMembers . length ? convertMap ( _ . first ( card . idMembers ) , config . member ) : null ,
293
+ due_at : card . due ,
294
+ followers : card . idMembers . length > 1 ? convertMap ( card . idMembers , config . member ) : [ ] ,
295
+ name : card . name ,
296
+ notes : card . desc ,
297
+ memberships : [ {
298
+ project : projectData . id ,
299
+ section : convertMap ( card . idList , listToSectionMap )
300
+ } ] ,
301
+ tags : card . idLabels . length ? convertMap ( card . idLabels , labelToTagMap ) : [ ] ,
302
+ projects : [ projectData . id ]
303
+ } ) . then ( result => {
304
+ var promises = [ ] ;
305
+ var taskData = result ;
306
+ cardToTaskMap [ card . id ] = result . id ;
307
+ countTask ++ ;
308
+
309
+ if ( countTask % 10 === 0 ) {
310
+ console . log ( `${ countTask } ...` ) ;
311
+ }
312
+
313
+ if ( card . idChecklists . length ) {
314
+ promises . push (
315
+ Promise . mapSeries ( convertMap ( card . idChecklists . reverse ( ) , checklistMap ) , checklist => {
316
+ return Promise . mapSeries ( checklist . checkItems . reverse ( ) , item => {
317
+ return client . tasks . addSubtask ( taskData . id , {
318
+ name : item . name ,
319
+ completed : item . state !== 'incomplete'
320
+ } ) ;
321
+ } ) . then ( function ( ) {
322
+ return client . tasks . addSubtask ( taskData . id , {
323
+ name : `${ checklist . name } :`
324
+ } ) ;
325
+ } ) ;
326
+ } )
327
+ ) ;
328
+ }
329
+
330
+ if ( parseInt ( card . badges . comments , 10 ) > 0 ) {
331
+ promises . push (
332
+ // Trello export has limitation for count of actions as 1000. so we need to request directly trello API.
333
+ trello . getAsync ( `/1/cards/${ card . id } /actions?limit=1000` ) . then ( result => {
334
+ var comments = _ . filter ( result , action => {
335
+ return action . type === 'commentCard' ;
336
+ } ) ;
337
+
338
+ return Promise . mapSeries ( comments . reverse ( ) , comment => {
339
+ var member = convertMap ( comment . idMemberCreator , config . member ) ;
340
+ var text = comment . data . text ;
341
+ var memberName = member ? convertMap ( member , userMap ) : comment . memberCreator . fullName ;
171
342
172
- console . log ( `Creating ${ file . cards . length } parent tasks...` ) ;
343
+ text = ` ${ memberName } : ${ text } from Trello` ;
173
344
174
- // Creates tasks
175
- return Promise . map ( file . cards , card => {
345
+ return client . tasks . addComment ( taskData . id , {
346
+ text : text
347
+ } ) ;
348
+ } ) ;
349
+ } )
350
+ ) ;
351
+ }
176
352
177
- } , { concurrency : 3 } ) ;
353
+ if ( card . attachments . length ) {
354
+ promises . push (
355
+ Promise . mapSeries ( card . attachments , attachment => {
356
+ return fetchImage ( attachment . url ) . then ( image => {
357
+ return uploadImageToAsana ( taskData . id , image , path . basename ( attachment . url ) ) ;
358
+ } ) ;
359
+ } )
360
+ ) ;
361
+ }
362
+
363
+ return Promise . all ( promises ) ;
364
+ } ) ;
365
+ } ) ;
366
+ } ) ;
367
+ } ) . then ( function ( ) {
368
+ console . log ( 'complete!' ) ;
178
369
} ) ;
179
370
} ) ;
180
371
} ) ;
372
+ } ) . catch ( reason => {
373
+ console . error ( reason ) ;
181
374
} ) . catch ( Promise . CancellationError , function ( reason ) {
182
375
// nothing to do
183
- } ) . catch ( function ( reason ) {
184
- console . error ( reason ) ;
185
376
} ) ;
0 commit comments