@@ -4,9 +4,17 @@ import SplitPane from "react-split-pane";
4
4
import { DataTreeView } from "./DataTreeView" ;
5
5
import { makeReactChildren , tokenizeANSIString } from "./Ansi" ;
6
6
import { Linkify } from "./Linkify" ;
7
- import { StageInfo } from "../../../pipeline-graph-view/pipeline-graph/main/" ;
7
+ import { StageInfo , Result } from "../../../pipeline-graph-view/pipeline-graph/main/" ;
8
8
import { StepInfo } from "./DataTreeView" ;
9
9
10
+ import Typography from '@material-ui/core/Typography' ;
11
+
12
+ import HourglassEmptyIcon from '@material-ui/icons/HourglassEmpty' ;
13
+ import ScheduleIcon from '@material-ui/icons/Schedule' ;
14
+ import TimerIcon from '@material-ui/icons/Timer' ;
15
+ import InfoIcon from '@material-ui/icons/Info' ;
16
+ import LinkIcon from '@material-ui/icons/Link' ;
17
+
10
18
import "./pipeline-console.scss" ;
11
19
12
20
interface PipelineConsoleProps { }
@@ -27,6 +35,70 @@ export interface ConsoleLineProps {
27
35
key : string ;
28
36
}
29
37
38
+ export interface StageSummaryProps {
39
+ stage : StageInfo ,
40
+ failedSteps : StepInfo [ ]
41
+ }
42
+
43
+
44
+ // Tree Item for stages
45
+ const StageSummary = ( props : StageSummaryProps ) => (
46
+ < React . Fragment >
47
+ < div className = "stage-detail-group" >
48
+ < Typography color = "inherit" className = "detail-element-header" > Stage '{ props . stage . name } '</ Typography >
49
+ < div className = "detail-element" key = "start-time" > < ScheduleIcon className = "detail-icon" /> { props . stage . startTimeMillis } </ div >
50
+ < div className = "detail-element" key = "paused-duration" > < HourglassEmptyIcon className = "detail-icon" /> { props . stage . pauseDurationMillis } </ div >
51
+ < div className = "detail-element" key = "duration" > < TimerIcon className = "detail-icon" /> { props . stage . totalDurationMillis } </ div >
52
+ < div className = "detail-element" key = "status" > < InfoIcon className = "detail-icon " /> < span className = "capitalize" > { props . stage . state } </ span > </ div >
53
+ {
54
+ props . failedSteps . map ( ( value : StepInfo ) => {
55
+ console . log ( `Found failed step ${ value } ` )
56
+ return ( < FailedStepLink step = { value } key = { `failed-step-link-${ value . id } ` } /> ) ;
57
+ } )
58
+ }
59
+ </ div >
60
+ </ React . Fragment >
61
+ ) ;
62
+
63
+ export interface StepSummaryProps {
64
+ step : StepInfo ,
65
+ }
66
+
67
+ // Tree Item for stages
68
+ const StepSummary = ( props : StepSummaryProps ) => (
69
+ < React . Fragment >
70
+ < div className = "step-detail-group" >
71
+ < div className = "detail-element" key = "start-time" > < ScheduleIcon className = "detail-icon" /> { props . step . startTimeMillis } </ div >
72
+ < div className = "detail-element" key = "paused-duration" > < HourglassEmptyIcon className = "detail-icon" /> { props . step . pauseDurationMillis } </ div >
73
+ < div className = "detail-element" key = "duration" > < TimerIcon className = "detail-icon" /> { props . step . totalDurationMillis } </ div >
74
+ < div className = "detail-element capitalize" key = "status" > < InfoIcon className = "detail-icon" /> < span className = "capitalize" > { props . step . state } </ span > </ div >
75
+ </ div >
76
+ </ React . Fragment >
77
+ ) ;
78
+
79
+ export interface FailedStepLinkProps {
80
+ step : StepInfo ,
81
+ }
82
+
83
+ const FailedStepLink = ( props : FailedStepLinkProps ) => (
84
+ < div className = "detail-element" >
85
+ < LinkIcon className = "detail-icon" />
86
+ < a
87
+ className = "detail-element"
88
+ href = { `?selected-node=${ props . step . id } ` }
89
+ >
90
+ Failed step: { props . step . name }
91
+ </ a >
92
+ </ div >
93
+ ) ;
94
+
95
+ export interface ConsoleLineProps {
96
+ lineNumber : string ;
97
+ content : ( string | JSX . Element ) [ ] ;
98
+ stepId : string ;
99
+ key : string ;
100
+ }
101
+
30
102
// Tree Item for stages
31
103
const ConsoleLine = ( props : ConsoleLineProps ) => (
32
104
< div className = "console-output-item" key = { props . lineNumber } >
@@ -136,6 +208,7 @@ export class PipelineConsole extends React.Component<
136
208
}
137
209
}
138
210
211
+
139
212
handleUrlParams ( ) {
140
213
console . debug ( `In handleUrlParams.` ) ;
141
214
let params = new URLSearchParams ( document . location . search . substring ( 1 ) ) ;
@@ -210,7 +283,6 @@ export class PipelineConsole extends React.Component<
210
283
getStageNodeHierarchy ( nodeId : string , stages : StageInfo [ ] ) : Array < string > {
211
284
for ( let i = 0 ; i < stages . length ; i ++ ) {
212
285
let stage = stages [ i ] ;
213
- console . log ( `Checking node id ${ stage . id } ` ) ;
214
286
if ( String ( stage . id ) == nodeId ) {
215
287
// Found the node, so start a list of expanded nodes - it will be this and it's ancestors.
216
288
return [ String ( stage . id ) ] ;
@@ -226,6 +298,84 @@ export class PipelineConsole extends React.Component<
226
298
return [ ] ;
227
299
}
228
300
301
+ renderStageDetails ( ) {
302
+ let focusedStage = null ;
303
+ for ( let i = 0 ; i < this . state . stages . length ; i ++ ) {
304
+ let stage = this . state . stages [ i ] ;
305
+ if ( '' + stage . id == this . state . selected ) {
306
+ // User has selected a stage node.
307
+ focusedStage = stage ;
308
+ let failedSteps = [ ] as StepInfo [ ] ;
309
+ for ( let i = 0 ; i < this . state . steps . length ; i ++ ) {
310
+ let step = this . state . steps [ i ] ;
311
+ if ( step . stageId === this . state . selected ) {
312
+ // We seem to get a mix of upper and lower case states, so normalise on lowercase.
313
+ if ( step . state . toLowerCase ( ) === "unstable" ) {
314
+ failedSteps . push ( step ) ;
315
+ }
316
+ }
317
+ }
318
+ return (
319
+ < div className = "console-output" >
320
+ < StageSummary stage = { focusedStage } failedSteps = { failedSteps } />
321
+ </ div >
322
+ ) ;
323
+ }
324
+ }
325
+ return (
326
+ // Return empty div
327
+ < div > </ div >
328
+ )
329
+ }
330
+
331
+ renderStepDetails ( ) {
332
+ for ( let i = 0 ; i < this . state . steps . length ; i ++ ) {
333
+ let step = this . state . steps [ i ] ;
334
+ if ( '' + step . id == this . state . selected ) {
335
+ return (
336
+ < div className = "console-output" >
337
+ < StepSummary step = { step } />
338
+ </ div >
339
+ ) ;
340
+ }
341
+ }
342
+ return (
343
+ // Return empty div
344
+ < div > </ div >
345
+ )
346
+ }
347
+
348
+ renderConsoleOutput ( ) {
349
+ if ( this . state . consoleText . length > 0 ) {
350
+ const lineChunks = this . state . consoleText
351
+ . split ( "\n" )
352
+ . map ( tokenizeANSIString )
353
+ . map ( makeReactChildren ) ;
354
+ return (
355
+ < div className = "console-output" >
356
+ < pre className = "console-pane console-output-item" >
357
+ {
358
+ lineChunks . map ( ( line , index ) => {
359
+ let lineNumber = String ( index + 1 ) ;
360
+ return (
361
+ < ConsoleLine
362
+ content = { line }
363
+ lineNumber = { lineNumber }
364
+ stepId = { this . state . selected }
365
+ key = { `${ this . state . selected } -${ lineNumber } ` }
366
+ />
367
+ ) ;
368
+ } ) }
369
+ </ pre >
370
+ </ div >
371
+ )
372
+ } else {
373
+ // Return empty div if no text.
374
+ return (
375
+ < div > </ div >
376
+ )
377
+ }
378
+ }
229
379
render ( ) {
230
380
const splitPaneStyle : React . CSSProperties = {
231
381
position : "relative" ,
@@ -237,16 +387,10 @@ export class PipelineConsole extends React.Component<
237
387
overflowY : "scroll" ,
238
388
} ;
239
389
240
- const lineChunks = this . state . consoleText
241
- . split ( "\n" )
242
- . map ( tokenizeANSIString )
243
- . map ( makeReactChildren ) ;
244
-
245
390
return (
246
391
< React . Fragment >
247
392
< div className = "App" >
248
393
< SplitPane
249
- split = "vertical"
250
394
minSize = { 150 }
251
395
defaultSize = { parseInt ( localStorage . getItem ( "splitPos" ) || "250" ) }
252
396
onChange = { ( size ) => localStorage . setItem ( "splitPos" , `${ size } ` ) }
@@ -262,20 +406,11 @@ export class PipelineConsole extends React.Component<
262
406
steps = { this . state . steps }
263
407
/>
264
408
</ div >
265
- < div className = "console-output" >
266
- < pre className = "console-pane console-output-item" >
267
- { lineChunks . map ( ( line , index ) => {
268
- let lineNumber = String ( index + 1 ) ;
269
- return (
270
- < ConsoleLine
271
- content = { line }
272
- lineNumber = { lineNumber }
273
- stepId = { this . state . selected }
274
- key = { `${ this . state . selected } -${ lineNumber } ` }
275
- />
276
- ) ;
277
- } ) }
278
- </ pre >
409
+
410
+ < div >
411
+ { this . renderStageDetails ( ) }
412
+ { this . renderStepDetails ( ) }
413
+ { this . renderConsoleOutput ( ) }
279
414
</ div >
280
415
</ SplitPane >
281
416
</ div >
0 commit comments