@@ -16,10 +16,8 @@ import { AWSTreeNodeBase } from '../../shared/treeview/nodes/awsTreeNodeBase'
1616import { PlaceholderNode } from '../../shared/treeview/nodes/placeholderNode'
1717import { makeChildrenNodes } from '../../shared/treeview/utils'
1818import { toArrayAsync , toMap , updateInPlace } from '../../shared/utilities/collectionUtils'
19- import { listStateMachines } from '../../stepFunctions/utils'
20- import { getIcon } from '../../shared/icons'
21-
22- export const contextValueStateMachine = 'awsStateMachineNode'
19+ import { listStateMachines , listExecutions } from '../../stepFunctions/utils'
20+ import { getIcon , IconPath } from '../../shared/icons'
2321
2422const sfnNodeMap = new Map < string , StepFunctionsNode > ( )
2523
@@ -74,20 +72,70 @@ export class StepFunctionsNode extends AWSTreeNodeBase {
7472 this . stateMachineNodes ,
7573 functions . keys ( ) ,
7674 ( key ) => this . stateMachineNodes . get ( key ) ! . update ( functions . get ( key ) ! ) ,
77- ( key ) => makeStateMachineNode ( this , this . regionCode , functions . get ( key ) ! )
75+ ( key ) => new StateMachineNode ( this , this . regionCode , functions . get ( key ) ! , this . client )
7876 )
7977 }
8078}
8179
80+ /**
81+ * Represents a Step Functions state machine in the Explorer view. This node
82+ * appears immediately underneath the "Step Functions" node. A StateMachineNode
83+ * will contain children of type StateMachineExecutionNode, representing
84+ * the most recent executions of that state machine.
85+ */
8286export class StateMachineNode extends AWSTreeNodeBase implements AWSResourceNode {
87+ public static readonly contextValue = 'awsStateMachineNode'
88+ public static readonly maxExecutionsToShow = 10
89+
90+ private readonly stateMachineExecutionNodes : Map < string , StateMachineExecutionNode >
91+
8392 public constructor (
8493 public readonly parent : AWSTreeNodeBase ,
8594 public override readonly regionCode : string ,
86- public details : StepFunctions . StateMachineListItem
95+ public details : StepFunctions . StateMachineListItem ,
96+ private readonly client : StepFunctionsClient
8797 ) {
88- super ( '' )
98+ super ( '' , vscode . TreeItemCollapsibleState . Collapsed )
99+ this . stateMachineExecutionNodes = new Map < string , StateMachineExecutionNode > ( )
89100 this . update ( details )
90101 this . iconPath = getIcon ( 'aws-stepfunctions-preview' )
102+ this . contextValue = StateMachineNode . contextValue
103+ }
104+
105+ public override async getChildren ( ) : Promise < AWSTreeNodeBase [ ] > {
106+ return await makeChildrenNodes ( {
107+ getChildNodes : async ( ) => {
108+ await this . updateChildren ( )
109+ return [ ...this . stateMachineExecutionNodes . values ( ) ]
110+ } ,
111+ getNoChildrenPlaceholderNode : async ( ) =>
112+ new PlaceholderNode (
113+ this ,
114+ localize ( 'AWS.explorerNode.stepfunctions.noStateMachineExecution' , '[No Executions found]' )
115+ ) ,
116+ /*
117+ * Note: although Step Functions returns the executions in the correct order, this sorting
118+ * is still needed to ensure newly added nodes (via updateChildren()) appear in the correct place.
119+ */
120+ sort : ( nodeA , nodeB ) => {
121+ const dateA = nodeA . details . startDate as Date // startDate will never be undefined.
122+ const dateB = nodeB . details . startDate as Date
123+ return dateB . getTime ( ) - dateA . getTime ( )
124+ } ,
125+ } )
126+ }
127+
128+ public async updateChildren ( ) : Promise < void > {
129+ const executions : Map < string , StepFunctions . ExecutionListItem > = toMap (
130+ await toArrayAsync ( listExecutions ( this . client , this . arn , StateMachineNode . maxExecutionsToShow ) ) ,
131+ ( details ) => details . name
132+ )
133+ updateInPlace (
134+ this . stateMachineExecutionNodes ,
135+ executions . keys ( ) ,
136+ ( key ) => this . stateMachineExecutionNodes . get ( key ) ! . update ( executions . get ( key ) ! ) ,
137+ ( key ) => new StateMachineExecutionNode ( this , this . regionCode , executions . get ( key ) ! )
138+ )
91139 }
92140
93141 public update ( details : StepFunctions . StateMachineListItem ) : void {
@@ -113,13 +161,61 @@ export class StateMachineNode extends AWSTreeNodeBase implements AWSResourceNode
113161 }
114162}
115163
116- function makeStateMachineNode (
117- parent : AWSTreeNodeBase ,
118- regionCode : string ,
119- details : StepFunctions . StateMachineListItem
120- ) : StateMachineNode {
121- const node = new StateMachineNode ( parent , regionCode , details )
122- node . contextValue = contextValueStateMachine
164+ /**
165+ * Represents a single execution of a Step Functions state machine in the Explorer
166+ * view. This node appears immediately underneath the corresponding StateMachineNode.
167+ */
168+ export class StateMachineExecutionNode extends AWSTreeNodeBase implements AWSResourceNode {
169+ public static contextValue = 'awsStateMachineExecutionNode'
170+
171+ public constructor (
172+ public readonly parent : AWSTreeNodeBase ,
173+ public override readonly regionCode : string ,
174+ public details : StepFunctions . ExecutionListItem
175+ ) {
176+ super ( '' )
177+ this . update ( details )
178+ this . contextValue = StateMachineExecutionNode . contextValue
179+ }
180+
181+ public update ( details : StepFunctions . ExecutionListItem ) : void {
182+ this . details = details
183+ this . label = this . details . name || ''
184+ this . tooltip = this . getToolTip ( this . details )
185+ this . iconPath = this . getIconPathForStatus ( this . details . status )
186+ }
187+
188+ public get arn ( ) : string {
189+ return this . details . executionArn || ''
190+ }
123191
124- return node
192+ public get name ( ) : string {
193+ return this . details . name || ''
194+ }
195+
196+ private getIconPathForStatus ( status ?: string ) : IconPath {
197+ switch ( status ) {
198+ case 'RUNNING' :
199+ return getIcon ( 'vscode-sync' )
200+ case 'SUCCEEDED' :
201+ return getIcon ( 'vscode-check' )
202+ default :
203+ return getIcon ( 'vscode-error' )
204+ }
205+ }
206+
207+ private getToolTip ( details : StepFunctions . ExecutionListItem ) {
208+ const startTimeText = localize ( 'AWS.explorerNode.stepfunctions.startTime' , 'Start Time' )
209+ const endTimeText = localize ( 'AWS.explorerNode.stepfunctions.endTime' , 'End Time' )
210+ const durationText = localize ( 'AWS.explorerNode.stepfunctions.duration' , 'Duration' )
211+ const secondsText = localize ( 'AWS.explorerNode.stepfunctions.seconds' , 'seconds' )
212+
213+ let text : string = `${ details . status } ${ os . EOL } ${ startTimeText } : ${ details . startDate ?. toLocaleString ( ) } ${ os . EOL } `
214+ if ( details . status !== 'RUNNING' ) {
215+ text += `${ endTimeText } : ${ details . stopDate ?. toLocaleString ( ) } ${ os . EOL } `
216+ const endDate = details . stopDate ? details . stopDate : new Date ( )
217+ text += `${ durationText } : ${ Math . trunc ( ( endDate . getTime ( ) - details . startDate ! . getTime ( ) ) / 1000 ) } ${ secondsText } ${ os . EOL } `
218+ }
219+ return text
220+ }
125221}
0 commit comments