1
1
require ( 'dotenv' ) . config ( ) ;
2
2
3
3
const Utils = require ( './utils' ) ;
4
+ const HawkCatcher = require ( '@hawk.so/nodejs' ) . default ;
4
5
5
6
const { Octokit } = require ( '@octokit/core' ) ;
6
7
const parseGithubUrl = require ( 'parse-github-url' ) ;
@@ -9,6 +10,8 @@ const axios = require('axios').default;
9
10
const CronJob = require ( 'cron' ) . CronJob ;
10
11
11
12
const TOKEN = process . env . TOKEN ;
13
+ const HAWK_TOKEN = process . env . HAWK_TOKEN ;
14
+
12
15
const COLUMN_NODE_ID_TO_DO = process . env . COLUMN_NODE_ID_TO_DO ;
13
16
const COLUMN_NODE_ID_PR = process . env . COLUMN_NODE_ID_PR ;
14
17
const NOTIFIER_URL = process . env . NOTIFIER_URL ;
@@ -40,6 +43,16 @@ const CARDS_QUERY = require('./queries/cards');
40
43
const ISSUE_QUERY = require ( './queries/issue' ) ;
41
44
const PR_QUERY = require ( './queries/pr' ) ;
42
45
46
+ /**
47
+ * Initialize HawkCatcher.
48
+ */
49
+ HawkCatcher . init ( {
50
+ token : HAWK_TOKEN ,
51
+ context : {
52
+ myOwnDebugInfo : '1234' ,
53
+ } ,
54
+ } ) ;
55
+
43
56
/**
44
57
* Sends POST request to telegram bot
45
58
*
@@ -144,28 +157,32 @@ function createReviewStatus(latestOpinionatedReviews, latestReviews, reviewReque
144
157
/**
145
158
* 💬 LatestReviews for adding commented status
146
159
*/
147
- latestReviews . nodes . reverse ( ) . forEach ( ( { state, author } ) => {
148
- const person = author . login ;
160
+ if ( Utils . isPropertyExist ( latestReviews , 'nodes' ) ) {
161
+ latestReviews . nodes . reverse ( ) . forEach ( ( { state, author } ) => {
162
+ const person = author . login ;
149
163
150
- reviewReport [ person ] = getReviewStateEmoji ( state ) ;
151
- } ) ;
164
+ reviewReport [ person ] = getReviewStateEmoji ( state ) ;
165
+ } ) ;
166
+ }
152
167
153
168
/**
154
169
* ✅❌ LatestOpinionatedReviews for the approved and changes requested
155
170
*/
156
- latestOpinionatedReviews . nodes . forEach ( ( { state, author } ) => {
157
- const person = author . login ;
158
-
159
- reviewReport [ person ] = getReviewStateEmoji ( state ) ;
160
- } ) ;
171
+ if ( Utils . isPropertyExist ( latestOpinionatedReviews , 'nodes' ) ) {
172
+ latestOpinionatedReviews . nodes . forEach ( ( { state, author } ) => {
173
+ const person = author . login ;
161
174
175
+ reviewReport [ person ] = getReviewStateEmoji ( state ) ;
176
+ } ) ;
177
+ }
162
178
/**
163
179
* 🔸 Requested review
164
180
*/
165
- reviewRequests . nodes . forEach ( ( { requestedReviewer : { login } } ) => {
166
- reviewReport [ login ] = getReviewStateEmoji ( ) ;
167
- } ) ;
168
-
181
+ if ( Utils . isPropertyExist ( reviewRequests , 'nodes' ) ) {
182
+ reviewRequests . nodes . forEach ( ( { requestedReviewer : { login } } ) => {
183
+ reviewReport [ login ] = getReviewStateEmoji ( ) ;
184
+ } ) ;
185
+ }
169
186
let reviewStatus = '' ;
170
187
171
188
Object . entries ( reviewReport ) . forEach ( ( [ login , state ] ) => {
@@ -196,15 +213,23 @@ function createTaskBadge(url) {
196
213
* @returns {string } - parsed message.
197
214
*/
198
215
function pullRequestParser ( content ) {
199
- const taskTitle = Utils . trimString ( escapeChars ( content . title ) , TRIM_PR_NAME_LENGHT ) ;
216
+ const {
217
+ title,
218
+ latestOpinionatedReviews,
219
+ latestReviews,
220
+ reviewRequests,
221
+ author,
222
+ url,
223
+ } = content ;
224
+
225
+ const taskTitle = Utils . trimString ( escapeChars ( title ) , TRIM_PR_NAME_LENGHT ) ;
200
226
const reviewState = createReviewStatus (
201
- content . latestOpinionatedReviews ,
202
- content . latestReviews ,
203
- content . reviewRequests
227
+ latestOpinionatedReviews ,
228
+ latestReviews ,
229
+ reviewRequests
204
230
) ;
205
231
206
- const parsedTask = `${ createTaskBadge ( content . url ) } : <a href="${ content . url
207
- } ">${ taskTitle } </a> ${ reviewState } @${ content . author . login } `;
232
+ const parsedTask = `${ createTaskBadge ( url ) } : <a href="${ url } ">${ taskTitle } </a> ${ reviewState } @${ author . login } ` ;
208
233
209
234
/**
210
235
* @todo discuss if it is necessary to duplicate links to pr
@@ -231,14 +256,20 @@ function pullRequestParser(content) {
231
256
* @returns {string } - parsed message.
232
257
*/
233
258
function issuesParser ( content ) {
234
- const taskTitle = Utils . trimString ( escapeChars ( content . title ) , TRIM_PR_NAME_LENGHT ) ;
235
-
236
- let parsedTask = `${ createTaskBadge ( content . url ) } : <a href="${ content . url
237
- } ">${ escapeChars ( taskTitle ) } </a>`;
238
-
239
- content . assignees . nodes . forEach ( ( node ) => {
240
- parsedTask += `@${ node . login } ` ;
241
- } ) ;
259
+ const {
260
+ title,
261
+ assignees,
262
+ url,
263
+ } = content ;
264
+ const taskTitle = Utils . trimString ( escapeChars ( title ) , TRIM_PR_NAME_LENGHT ) ;
265
+
266
+ let parsedTask = `${ createTaskBadge ( url ) } : <a href="${ url } ">${ escapeChars ( taskTitle ) } </a>` ;
267
+
268
+ if ( Utils . isPropertyExist ( assignees , 'nodes' ) ) {
269
+ assignees . nodes . forEach ( ( { login } ) => {
270
+ parsedTask += `@${ login } ` ;
271
+ } ) ;
272
+ }
242
273
243
274
return parsedTask ;
244
275
}
@@ -273,10 +304,12 @@ async function parseGithubLink(message, parsable) {
273
304
number : parseInt ( id ) ,
274
305
} ) ;
275
306
276
- return replaceGithubLink (
277
- message ,
278
- pullRequestParser ( response . repository . pullRequest )
279
- ) ;
307
+ if ( Utils . isPropertyExist ( response , 'repository' , 'pullRequest' ) ) {
308
+ return replaceGithubLink (
309
+ message ,
310
+ pullRequestParser ( response . repository . pullRequest )
311
+ ) ;
312
+ }
280
313
}
281
314
if ( type === 'issues' ) {
282
315
const response = await graphqlQuery ( ISSUE_QUERY , {
@@ -285,7 +318,9 @@ async function parseGithubLink(message, parsable) {
285
318
number : parseInt ( id ) ,
286
319
} ) ;
287
320
288
- return replaceGithubLink ( message , issuesParser ( response . repository . issue ) ) ;
321
+ if ( Utils . isPropertyExist ( response , 'repository' , 'issue' ) ) {
322
+ return replaceGithubLink ( message , issuesParser ( response . repository . issue ) ) ;
323
+ }
289
324
}
290
325
}
291
326
@@ -298,34 +333,47 @@ async function parseGithubLink(message, parsable) {
298
333
*/
299
334
async function parseQuery ( members , response ) {
300
335
const parsedCardData = await Promise . all (
301
- await response . map ( async ( items ) => {
302
- if ( items . state === 'NOTE_ONLY' ) {
303
- for ( let i = 0 ; i < members . length ; i ++ ) {
304
- if ( items . note . includes ( `@${ members [ i ] . name } ` ) ) {
305
- const parsable = checkForParsableGithubLink ( items . note ) ;
306
-
307
- return parsable [ 0 ]
308
- ? await parseGithubLink ( items . note , parsable )
309
- : escapeChars ( items . note ) ;
336
+ await response . map ( async ( cardData ) => {
337
+ try {
338
+ if ( Utils . isPropertyExist ( cardData , 'state' ) ) {
339
+ if ( cardData . state === 'NOTE_ONLY' ) {
340
+ if ( Utils . isPropertyExist ( cardData , 'note' ) && Utils . isPropertyExist ( cardData , 'creator' ) ) {
341
+ for ( let i = 0 ; i < members . length ; i ++ ) {
342
+ if ( cardData . note . includes ( `@${ members [ i ] . name } ` ) ) {
343
+ const parsable = checkForParsableGithubLink ( cardData . note ) ;
344
+
345
+ return parsable [ 0 ]
346
+ ? await parseGithubLink ( cardData . note , parsable )
347
+ : escapeChars ( cardData . note ) ;
348
+ }
349
+ }
350
+ const parsable = checkForParsableGithubLink ( cardData . note ) ;
351
+
352
+ return parsable [ 0 ]
353
+ ? await parseGithubLink ( cardData . note , parsable )
354
+ : `${ cardData . note } @${ cardData . creator . login } ` ;
355
+ }
356
+ } else if ( cardData . state === 'CONTENT_ONLY' ) {
357
+ if ( Utils . isPropertyExist ( cardData , 'content' , '__typename' ) ) {
358
+ if ( cardData . content . __typename === 'PullRequest' ) {
359
+ return pullRequestParser ( cardData . content ) ;
360
+ }
361
+
362
+ if ( cardData . content . __typename === 'Issue' ) {
363
+ return issuesParser ( cardData . content ) ;
364
+ }
365
+ }
310
366
}
367
+
368
+ return '' ;
311
369
}
312
- const parsable = checkForParsableGithubLink ( items . note ) ;
313
-
314
- return parsable [ 0 ]
315
- ? await parseGithubLink ( items . note , parsable )
316
- : `${ items . note } @${ items . creator . login } ` ;
317
- } else if ( items . state === 'CONTENT_ONLY' ) {
318
- if ( items . content . __typename === 'PullRequest' ) {
319
- return pullRequestParser ( items . content ) ;
320
- }
321
- if ( items . content . __typename === 'Issue' ) {
322
- return issuesParser ( items . content ) ;
323
- }
370
+ } catch ( e ) {
371
+ HawkCatcher . send ( e , {
372
+ cardData : cardData ,
373
+ } ) ;
324
374
}
325
-
326
- return '' ;
327
375
} )
328
- ) ;
376
+ ) . catch ( HawkCatcher . send ) ;
329
377
330
378
let cardDataWithoutMembers = [ ...parsedCardData ] ;
331
379
@@ -338,9 +386,9 @@ async function parseQuery(members, response) {
338
386
x . replace ( / ^ \s + | \s + $ / g, '' )
339
387
) ;
340
388
341
- parsedCardData . forEach ( ( items , index ) => {
389
+ parsedCardData . forEach ( ( cardData , index ) => {
342
390
for ( let i = 0 ; i < members . length ; i ++ ) {
343
- if ( items . includes ( `@${ members [ i ] . name } ` ) ) {
391
+ if ( cardData . includes ( `@${ members [ i ] . name } ` ) ) {
344
392
members [ i ] . tasks . push ( cardDataWithoutMembers [ index ] ) ;
345
393
}
346
394
}
@@ -359,9 +407,9 @@ function getMembersName(memberList) {
359
407
const members = [ ] ;
360
408
361
409
if ( memberList ) {
362
- memberList . split ( ' ' ) . forEach ( ( items ) => {
410
+ memberList . split ( ' ' ) . forEach ( ( memberName ) => {
363
411
members . push ( {
364
- name : items ,
412
+ name : memberName ,
365
413
tasks : [ ] ,
366
414
} ) ;
367
415
} ) ;
@@ -370,9 +418,9 @@ function getMembersName(memberList) {
370
418
}
371
419
372
420
return octokit . graphql ( MEMBERS_QUERY ) . then ( ( query ) => {
373
- query . organization . membersWithRole . nodes . forEach ( ( items ) => {
421
+ query . organization . membersWithRole . nodes . forEach ( ( { login } ) => {
374
422
members . push ( {
375
- name : items . login ,
423
+ name : login ,
376
424
tasks : [ ] ,
377
425
} ) ;
378
426
} ) ;
@@ -393,25 +441,29 @@ function getMembersName(memberList) {
393
441
async function notifyMessage ( title , columnID , includePersonWithNoTask = false ) {
394
442
let dataToSend = title + ' \n\n' ;
395
443
const queryResponse = await graphqlQuery ( CARDS_QUERY , { id : columnID } ) ;
396
- const parsedData = await parseQuery (
397
- getMembersName ( MENTION ) ,
398
- queryResponse . node . cards . nodes
399
- ) ;
444
+ let parsedData = { } ;
445
+
446
+ if ( Utils . isPropertyExist ( queryResponse , 'node' , 'cards' , 'nodes' ) ) {
447
+ parsedData = await parseQuery (
448
+ getMembersName ( MENTION ) ,
449
+ queryResponse . node . cards . nodes
450
+ ) ;
451
+ }
400
452
const personWithNoTask = [ ] ;
401
453
402
- parsedData . forEach ( ( items ) => {
454
+ parsedData . forEach ( ( { tasks , name } ) => {
403
455
/** Skip person with no tasks */
404
- if ( ! items . tasks . length ) {
405
- if ( includePersonWithNoTask && items . name != 'dependabot' ) {
406
- personWithNoTask . push ( items . name ) ;
456
+ if ( ! tasks . length ) {
457
+ if ( includePersonWithNoTask && name != 'dependabot' ) {
458
+ personWithNoTask . push ( name ) ;
407
459
}
408
460
409
461
return ;
410
462
}
411
463
412
- dataToSend += `<b>${ items . name } </b>\n` ;
464
+ dataToSend += `<b>${ name } </b>\n` ;
413
465
414
- items . tasks . forEach ( ( data ) => {
466
+ tasks . forEach ( ( data ) => {
415
467
dataToSend += `• ${ data } \n` ;
416
468
} ) ;
417
469
@@ -439,8 +491,8 @@ async function notifyMessage(title, columnID, includePersonWithNoTask = false) {
439
491
function parseMeetingMessage ( mentionList ) {
440
492
let message = `☝️ Join the meeting in Discord!\n\n` ;
441
493
442
- mentionList . split ( ' ' ) . forEach ( ( items ) => {
443
- message += `@${ items } ` ;
494
+ mentionList . split ( ' ' ) . forEach ( ( mentionName ) => {
495
+ message += `@${ mentionName } ` ;
444
496
} ) ;
445
497
446
498
return message ;
@@ -457,7 +509,7 @@ async function main() {
457
509
await notifyMessage ( "📌 Sprint's backlog" , COLUMN_NODE_ID_TO_DO , true )
458
510
)
459
511
. then ( ( ) => console . log ( 'Tasks Job Completed.' ) )
460
- . catch ( console . error ) ;
512
+ . catch ( HawkCatcher . send ) ;
461
513
} ,
462
514
null ,
463
515
true ,
@@ -471,7 +523,7 @@ async function main() {
471
523
await notifyMessage ( '👀 Pull requests for review' , COLUMN_NODE_ID_PR )
472
524
)
473
525
. then ( ( ) => console . log ( 'PR Job Completed.' ) )
474
- . catch ( console . error ) ;
526
+ . catch ( HawkCatcher . send ) ;
475
527
} ,
476
528
null ,
477
529
true ,
@@ -482,7 +534,7 @@ async function main() {
482
534
( ) => {
483
535
notify ( parseMeetingMessage ( MEETING_MENTION ) )
484
536
. then ( ( ) => console . log ( 'Meeting Job Completed.' ) )
485
- . catch ( console . error ) ;
537
+ . catch ( HawkCatcher . send ) ;
486
538
} ,
487
539
null ,
488
540
true ,
0 commit comments