1
+ // @ts -nocheck
1
2
// @ts -ignore
2
3
import blessed from 'blessed'
3
4
import contrib from 'blessed-contrib'
@@ -120,6 +121,52 @@ function setupCommand (name: string, description: string, argv: readonly string[
120
121
}
121
122
}
122
123
124
+ const METRICS = [
125
+ 'total_critical_alerts' ,
126
+ 'total_high_alerts' ,
127
+ 'total_medium_alerts' ,
128
+ 'total_low_alerts' ,
129
+ 'total_critical_added' ,
130
+ 'total_medium_added' ,
131
+ 'total_low_added' ,
132
+ 'total_high_added' ,
133
+ 'total_critical_prevented' ,
134
+ 'total_high_prevented' ,
135
+ 'total_medium_prevented' ,
136
+ 'total_low_prevented'
137
+ ]
138
+
139
+ type AnalyticsData = {
140
+ id : number ,
141
+ created_at : string
142
+ repository_id : string
143
+ organization_id : number
144
+ repository_name : string
145
+ total_critical_alerts : number
146
+ total_high_alerts : number
147
+ total_medium_alerts : number
148
+ total_low_alerts : number
149
+ total_critical_added : number
150
+ total_high_added : number
151
+ total_medium_added : number
152
+ total_low_added : number
153
+ total_critical_prevented : number
154
+ total_high_prevented : number
155
+ total_medium_prevented : number
156
+ total_low_prevented : number
157
+ top_five_alert_types : {
158
+ [ key : string ] : number
159
+ }
160
+ }
161
+
162
+ type FormattedAnalyticsData = {
163
+ [ key : string ] : {
164
+ [ key : string ] : number | {
165
+ [ key : string ] : number
166
+ }
167
+ }
168
+ }
169
+
123
170
async function fetchOrgAnalyticsData ( time : number , spinner : Ora , apiKey : string , outputJson : boolean ) : Promise < void > {
124
171
const socketSdk = await setupSdk ( apiKey )
125
172
const result = await handleApiCall ( socketSdk . getOrgAnalytics ( time . toString ( ) ) , 'fetching analytics data' )
@@ -134,15 +181,115 @@ async function fetchOrgAnalyticsData (time: number, spinner: Ora, apiKey: string
134
181
return console . log ( 'No analytics data is available for this organization yet.' )
135
182
}
136
183
137
- const data = formatData ( result . data )
184
+ const data = formatData ( result . data , 'org' )
185
+
186
+ console . log ( data )
138
187
139
188
if ( outputJson ) {
140
- return console . log ( data )
189
+ return console . log ( result . data )
141
190
}
142
191
143
192
return displayAnalyticsScreen ( data )
144
193
}
145
194
195
+ const months = [
196
+ 'Jan' ,
197
+ 'Feb' ,
198
+ 'Mar' ,
199
+ 'Apr' ,
200
+ 'May' ,
201
+ 'Jun' ,
202
+ 'Jul' ,
203
+ 'Aug' ,
204
+ 'Sep' ,
205
+ 'Oct' ,
206
+ 'Nov' ,
207
+ 'Dec'
208
+ ]
209
+
210
+ const formatDate = ( date : string ) => {
211
+ return `${ months [ new Date ( date ) . getMonth ( ) ] } ${ new Date ( date ) . getDate ( ) } `
212
+ }
213
+
214
+ const formatData = ( data : AnalyticsData [ ] , scope : string ) => {
215
+ let formattedData , sortedTopFivealerts
216
+
217
+ if ( scope === 'org' ) {
218
+ const topFiveAlerts = data . map ( d => d . top_five_alert_types || { } )
219
+
220
+ const totalTopAlerts = topFiveAlerts . reduce ( ( acc , current : { [ key : string ] : number } ) => {
221
+ const alertTypes = Object . keys ( current )
222
+ alertTypes . map ( ( type : string ) => {
223
+ if ( ! acc [ type ] ) {
224
+ acc [ type ] = current [ type ]
225
+ } else {
226
+ acc [ type ] += current [ type ]
227
+ }
228
+ return acc
229
+ } )
230
+ return acc
231
+ } , { } as { [ k : string ] : any } )
232
+
233
+
234
+ sortedTopFivealerts = Object . entries ( totalTopAlerts )
235
+ . sort ( ( [ , a ] , [ , b ] ) => b - a )
236
+ . slice ( 0 , 5 )
237
+ . reduce ( ( r , [ k , v ] ) => ( { ...r , [ k ] : v } ) , { } )
238
+
239
+ const formatData = ( label : string ) => {
240
+ return data . reduce ( ( acc , current ) => {
241
+ const date : string = formatDate ( current . created_at )
242
+ if ( ! acc [ date ] ) {
243
+ acc [ date ] = current [ label ]
244
+ } else {
245
+ acc [ date ] += current [ label ]
246
+ }
247
+ return acc
248
+ } , { } as { [ k : string ] : number } )
249
+ }
250
+
251
+ formattedData = METRICS . reduce ( ( acc , current : string ) => {
252
+ acc [ current ] = formatData ( current )
253
+ return acc
254
+ } , { } as { [ k : string ] : any } )
255
+
256
+ } else if ( scope === 'repo' ) {
257
+
258
+ const topAlerts = data . reduce ( ( acc , current ) => {
259
+ const alertTypes = Object . keys ( current . top_five_alert_types )
260
+ alertTypes . map ( type => {
261
+ if ( ! acc [ type ] ) {
262
+ acc [ type ] = current . top_five_alert_types [ type ]
263
+ } else {
264
+ if ( current . top_five_alert_types [ type ] > acc [ type ] ) {
265
+ acc [ type ] = current . top_five_alert_types [ type ]
266
+ }
267
+ }
268
+ return acc
269
+ } )
270
+ return acc
271
+ } , { } as { [ key : string ] : number } )
272
+
273
+ sortedTopFivealerts = Object . entries ( topAlerts )
274
+ . sort ( ( [ , a ] : [ string , number ] , [ , b ] : [ string , number ] ) => b - a )
275
+ . slice ( 0 , 5 )
276
+ . reduce ( ( r , [ k , v ] ) => ( { ...r , [ k ] : v } ) , { } )
277
+
278
+ formattedData = data . reduce ( ( acc , current ) => {
279
+ METRICS . forEach ( ( m : string ) => {
280
+ if ( ! acc [ m ] ) {
281
+ acc [ m ] = { }
282
+ }
283
+ acc [ m ] [ formatDate ( current . created_at ) ] = current [ m ]
284
+ return acc
285
+ } )
286
+ return acc
287
+ } , { } as { [ k : string ] : any } )
288
+ }
289
+
290
+ return { ...formattedData , top_five_alert_types : sortedTopFivealerts }
291
+ }
292
+
146
293
async function fetchRepoAnalyticsData ( repo : string , time : number , spinner : Ora , apiKey : string , outputJson : boolean ) : Promise < void > {
147
294
const socketSdk = await setupSdk ( apiKey )
148
295
const result = await handleApiCall ( socketSdk . getRepoAnalytics ( repo , time . toString ( ) ) , 'fetching analytics data' )
@@ -156,21 +303,48 @@ async function fetchRepoAnalyticsData (repo: string, time: number, spinner: Ora,
156
303
return console . log ( 'No analytics data is available for this organization yet.' )
157
304
}
158
305
159
- const data = formatData ( result . data )
306
+ const data = formatData ( result . data , 'repo' )
160
307
161
308
if ( outputJson ) {
162
- return console . log ( data )
309
+ return console . log ( result . data )
163
310
}
164
311
165
312
return displayAnalyticsScreen ( data )
166
313
}
167
314
168
- const renderLineCharts = ( grid : any , screen : any , title : string , coords : number [ ] , data : FormattedAnalyticsData , label : string ) => {
169
- const formattedDates = Object . keys ( data ) . map ( d => `${ new Date ( d ) . getMonth ( ) + 1 } /${ new Date ( d ) . getDate ( ) } ` )
315
+ const displayAnalyticsScreen = ( data : FormattedAnalyticsData ) => {
316
+ const screen = blessed . screen ( )
317
+ // eslint-disable-next-line
318
+ const grid = new contrib . grid ( { rows : 5 , cols : 4 , screen} )
170
319
171
- // @ts -ignore
172
- const alertsCounts = Object . values ( data ) . map ( d => d [ label ] )
173
-
320
+ renderLineCharts ( grid , screen , 'Total critical alerts' , [ 0 , 0 , 1 , 2 ] , data [ 'total_critical_alerts' ] )
321
+ renderLineCharts ( grid , screen , 'Total high alerts' , [ 0 , 2 , 1 , 2 ] , data [ 'total_high_alerts' ] )
322
+ renderLineCharts ( grid , screen , 'Total critical alerts added to the main branch' , [ 1 , 0 , 1 , 2 ] , data [ 'total_critical_added' ] )
323
+ renderLineCharts ( grid , screen , 'Total high alerts added to the main branch' , [ 1 , 2 , 1 , 2 ] , data [ 'total_high_added' ] )
324
+ renderLineCharts ( grid , screen , 'Total critical alerts prevented from the main branch' , [ 2 , 0 , 1 , 2 ] , data [ 'total_critical_prevented' ] )
325
+ renderLineCharts ( grid , screen , 'Total high alerts prevented from the main branch' , [ 2 , 2 , 1 , 2 ] , data [ 'total_high_prevented' ] )
326
+ renderLineCharts ( grid , screen , 'Total medium alerts prevented from the main branch' , [ 3 , 0 , 1 , 2 ] , data [ 'total_medium_prevented' ] )
327
+ renderLineCharts ( grid , screen , 'Total low alerts prevented from the main branch' , [ 3 , 2 , 1 , 2 ] , data [ 'total_low_prevented' ] )
328
+
329
+ const bar = grid . set ( 4 , 0 , 1 , 2 , contrib . bar ,
330
+ { label : 'Top 5 alert types'
331
+ , barWidth : 10
332
+ , barSpacing : 17
333
+ , xOffset : 0
334
+ , maxHeight : 9 , barBgColor : 'magenta' } )
335
+
336
+ screen . append ( bar ) //must append before setting data
337
+
338
+ bar . setData (
339
+ { titles : Object . keys ( data . top_five_alert_types )
340
+ , data : Object . values ( data . top_five_alert_types ) } )
341
+
342
+ screen . render ( )
343
+
344
+ screen . key ( [ 'escape' , 'q' , 'C-c' ] , ( ) => process . exit ( 0 ) )
345
+ }
346
+
347
+ const renderLineCharts = ( grid : any , screen : any , title : string , coords : number [ ] , data : { [ key : string ] : number } ) => {
174
348
const line = grid . set ( ...coords , contrib . line ,
175
349
{ style :
176
350
{ line : "cyan" ,
@@ -191,117 +365,9 @@ const renderLineCharts = (grid: any, screen: any, title: string, coords: number[
191
365
screen . append ( line )
192
366
193
367
const lineData = {
194
- x : formattedDates ,
195
- y : alertsCounts
368
+ x : Object . keys ( data ) ,
369
+ y : Object . values ( data )
196
370
}
197
371
198
372
line . setData ( [ lineData ] )
199
- }
200
-
201
- type AnalyticsData = {
202
- id : number ,
203
- created_at : string
204
- repository_id : string
205
- organization_id : number
206
- repository_name : string
207
- total_critical_alerts : number
208
- total_high_alerts : number
209
- total_medium_alerts : number
210
- total_low_alerts : number
211
- total_critical_added : number
212
- total_high_added : number
213
- total_medium_added : number
214
- total_low_added : number
215
- total_critical_prevented : number
216
- total_high_prevented : number
217
- total_medium_prevented : number
218
- total_low_prevented : number
219
- top_five_alert_types : {
220
- [ key : string ] : number
221
- }
222
- }
223
-
224
- type FormattedAnalyticsData = {
225
- [ key : string ] : AnalyticsData
226
- }
227
-
228
- const formatData = ( data : AnalyticsData [ ] ) => {
229
- return data . reduce ( ( acc : { [ key : string ] : any } , current ) => {
230
- const formattedDate = new Date ( current . created_at ) . toLocaleDateString ( )
231
-
232
- if ( acc [ formattedDate ] ) {
233
- acc [ formattedDate ] . total_critical_alerts += current . total_critical_alerts
234
- acc [ formattedDate ] . total_high_alerts += current . total_high_alerts
235
- acc [ formattedDate ] . total_critical_added += current . total_critical_added
236
- acc [ formattedDate ] . total_high_added += current . total_high_added
237
- acc [ formattedDate ] . total_critical_prevented += current . total_critical_prevented
238
- acc [ formattedDate ] . total_high_prevented += current . total_high_prevented
239
- acc [ formattedDate ] . total_medium_prevented += current . total_medium_prevented
240
- acc [ formattedDate ] . total_low_prevented += current . total_low_prevented
241
- } else {
242
- acc [ formattedDate ] = current
243
- acc [ formattedDate ] . created_at = formattedDate
244
- }
245
-
246
- return acc
247
- } , { } )
248
- }
249
-
250
- const displayAnalyticsScreen = ( data : FormattedAnalyticsData ) => {
251
- const screen = blessed . screen ( )
252
- // eslint-disable-next-line
253
- const grid = new contrib . grid ( { rows : 5 , cols : 4 , screen} )
254
-
255
- renderLineCharts ( grid , screen , 'Total critical alerts' , [ 0 , 0 , 1 , 2 ] , data , 'total_critical_alerts' )
256
- renderLineCharts ( grid , screen , 'Total high alerts' , [ 0 , 2 , 1 , 2 ] , data , 'total_high_alerts' )
257
- renderLineCharts ( grid , screen , 'Total critical alerts added to the main branch' , [ 1 , 0 , 1 , 2 ] , data , 'total_critical_added' )
258
- renderLineCharts ( grid , screen , 'Total high alerts added to the main branch' , [ 1 , 2 , 1 , 2 ] , data , 'total_high_added' )
259
- renderLineCharts ( grid , screen , 'Total critical alerts prevented from the main branch' , [ 2 , 0 , 1 , 2 ] , data , 'total_critical_prevented' )
260
- renderLineCharts ( grid , screen , 'Total high alerts prevented from the main branch' , [ 2 , 2 , 1 , 2 ] , data , 'total_high_prevented' )
261
- renderLineCharts ( grid , screen , 'Total medium alerts prevented from the main branch' , [ 3 , 0 , 1 , 2 ] , data , 'total_medium_prevented' )
262
- renderLineCharts ( grid , screen , 'Total low alerts prevented from the main branch' , [ 3 , 2 , 1 , 2 ] , data , 'total_low_prevented' )
263
-
264
- const bar = grid . set ( 4 , 0 , 1 , 2 , contrib . bar ,
265
- { label : 'Top 5 alert types'
266
- , barWidth : 10
267
- , barSpacing : 17
268
- , xOffset : 0
269
- , maxHeight : 9 , barBgColor : 'magenta' } )
270
-
271
- screen . append ( bar ) //must append before setting data
272
-
273
- const top5 = extractTop5Alerts ( data )
274
-
275
- bar . setData (
276
- { titles : Object . keys ( top5 )
277
- , data : Object . values ( top5 ) } )
278
-
279
- screen . render ( )
280
-
281
- screen . key ( [ 'escape' , 'q' , 'C-c' ] , ( ) => process . exit ( 0 ) )
282
- }
283
-
284
- const extractTop5Alerts = ( data : FormattedAnalyticsData ) => {
285
- const allTop5Alerts = Object . values ( data ) . map ( d => d . top_five_alert_types )
286
-
287
- const aggTop5Alerts = allTop5Alerts . reduce ( ( acc , current ) => {
288
- const alertTypes = Object . keys ( current )
289
-
290
- alertTypes . forEach ( type => {
291
- if ( ! acc [ type ] ) {
292
- // @ts -ignore
293
- acc [ type ] = current [ type ]
294
- } else {
295
- // @ts -ignore
296
- if ( acc [ type ] < current [ type ] ) {
297
- // @ts -ignore
298
- acc [ type ] = current [ type ]
299
- }
300
- }
301
- } )
302
-
303
- return acc
304
- } , { } )
305
-
306
- return Object . fromEntries ( Object . entries ( aggTop5Alerts ) . sort ( ( a : [ string , number ] , b : [ string , number ] ) => b [ 1 ] - a [ 1 ] ) . slice ( 0 , 5 ) )
307
373
}
0 commit comments