@@ -13,38 +13,40 @@ export enum WorkspaceQuery {
13
13
14
14
export class WorkspaceProvider implements vscode . TreeDataProvider < vscode . TreeItem > {
15
15
private workspaces : WorkspaceTreeItem [ ] = [ ]
16
- private agentMetadata : Record < WorkspaceAgent [ "id" ] , AgentMetadataEvent [ ] > = { }
16
+ private agentWatchers : Record < WorkspaceAgent [ "id" ] , { dispose : ( ) => void ; metadata ?: AgentMetadataEvent [ ] } > = { }
17
17
18
18
constructor ( private readonly getWorkspacesQuery : WorkspaceQuery , private readonly storage : Storage ) {
19
- getWorkspaces ( { q : this . getWorkspacesQuery } )
20
- . then ( ( workspaces ) => {
21
- const workspacesTreeItem : WorkspaceTreeItem [ ] = [ ]
22
- workspaces . workspaces . forEach ( ( workspace ) => {
23
- const showMetadata = this . getWorkspacesQuery === WorkspaceQuery . Mine
24
- if ( showMetadata ) {
25
- const agents = extractAgents ( workspace )
26
- agents . forEach ( ( agent ) => this . monitorMetadata ( agent . id ) ) // monitor metadata for all agents
27
- }
28
- const treeItem = new WorkspaceTreeItem (
29
- workspace ,
30
- this . getWorkspacesQuery === WorkspaceQuery . All ,
31
- showMetadata ,
32
- )
33
- workspacesTreeItem . push ( treeItem )
34
- } )
35
- return workspacesTreeItem
36
- } )
37
- . then ( ( workspaces ) => {
38
- this . workspaces = workspaces
39
- this . refresh ( )
19
+ this . fetchAndRefresh ( )
20
+ }
21
+
22
+ // fetchAndRefrehsh fetches new workspaces then re-renders the entire tree.
23
+ async fetchAndRefresh ( ) {
24
+ const token = await this . storage . getSessionToken ( )
25
+ const workspacesTreeItem : WorkspaceTreeItem [ ] = [ ]
26
+ Object . values ( this . agentWatchers ) . forEach ( ( watcher ) => watcher . dispose ( ) )
27
+ // If the URL is set then we are logged in.
28
+ if ( this . storage . getURL ( ) ) {
29
+ const resp = await getWorkspaces ( { q : this . getWorkspacesQuery } )
30
+ resp . workspaces . forEach ( ( workspace ) => {
31
+ const showMetadata = this . getWorkspacesQuery === WorkspaceQuery . Mine
32
+ if ( showMetadata && token ) {
33
+ const agents = extractAgents ( workspace )
34
+ agents . forEach ( ( agent ) => this . monitorMetadata ( agent . id , token ) ) // monitor metadata for all agents
35
+ }
36
+ const treeItem = new WorkspaceTreeItem ( workspace , this . getWorkspacesQuery === WorkspaceQuery . All , showMetadata )
37
+ workspacesTreeItem . push ( treeItem )
40
38
} )
39
+ }
40
+ this . workspaces = workspacesTreeItem
41
+ this . refresh ( )
41
42
}
42
43
43
44
private _onDidChangeTreeData : vscode . EventEmitter < vscode . TreeItem | undefined | null | void > =
44
45
new vscode . EventEmitter < vscode . TreeItem | undefined | null | void > ( )
45
46
readonly onDidChangeTreeData : vscode . Event < vscode . TreeItem | undefined | null | void > =
46
47
this . _onDidChangeTreeData . event
47
48
49
+ // refresh causes the tree to re-render. It does not fetch fresh workspaces.
48
50
refresh ( item : vscode . TreeItem | undefined | null | void ) : void {
49
51
this . _onDidChangeTreeData . fire ( item )
50
52
}
@@ -62,7 +64,7 @@ export class WorkspaceProvider implements vscode.TreeDataProvider<vscode.TreeIte
62
64
)
63
65
return Promise . resolve ( agentTreeItems )
64
66
} else if ( element instanceof AgentTreeItem ) {
65
- const savedMetadata = this . agentMetadata [ element . agent . id ] || [ ]
67
+ const savedMetadata = this . agentWatchers [ element . agent . id ] ?. metadata || [ ]
66
68
return Promise . resolve ( savedMetadata . map ( ( metadata ) => new AgentMetadataTreeItem ( metadata ) ) )
67
69
}
68
70
@@ -71,30 +73,39 @@ export class WorkspaceProvider implements vscode.TreeDataProvider<vscode.TreeIte
71
73
return Promise . resolve ( this . workspaces )
72
74
}
73
75
74
- async monitorMetadata ( agentId : WorkspaceAgent [ "id" ] ) : Promise < void > {
76
+ // monitorMetadata opens an SSE endpoint to monitor metadata on the specified
77
+ // agent and registers a disposer that can be used to stop the watch.
78
+ monitorMetadata ( agentId : WorkspaceAgent [ "id" ] , token : string ) : void {
75
79
const agentMetadataURL = new URL ( `${ this . storage . getURL ( ) } /api/v2/workspaceagents/${ agentId } /watch-metadata` )
76
80
const agentMetadataEventSource = new EventSource ( agentMetadataURL . toString ( ) , {
77
81
headers : {
78
- "Coder-Session-Token" : await this . storage . getSessionToken ( ) ,
82
+ "Coder-Session-Token" : token ,
79
83
} ,
80
84
} )
81
85
86
+ this . agentWatchers [ agentId ] = {
87
+ dispose : ( ) => {
88
+ delete this . agentWatchers [ agentId ]
89
+ agentMetadataEventSource . close ( )
90
+ } ,
91
+ }
92
+
82
93
agentMetadataEventSource . addEventListener ( "data" , ( event ) => {
83
94
try {
84
95
const dataEvent = JSON . parse ( event . data )
85
96
const agentMetadata = AgentMetadataEventSchemaArray . parse ( dataEvent )
86
97
87
98
if ( agentMetadata . length === 0 ) {
88
- agentMetadataEventSource . close ( )
99
+ this . agentWatchers [ agentId ] . dispose ( )
89
100
}
90
101
91
- const savedMetadata = this . agentMetadata [ agentId ]
102
+ const savedMetadata = this . agentWatchers [ agentId ] . metadata
92
103
if ( JSON . stringify ( savedMetadata ) !== JSON . stringify ( agentMetadata ) ) {
93
- this . agentMetadata [ agentId ] = agentMetadata // overwrite existing metadata
104
+ this . agentWatchers [ agentId ] . metadata = agentMetadata // overwrite existing metadata
94
105
this . refresh ( )
95
106
}
96
107
} catch ( error ) {
97
- agentMetadataEventSource . close ( )
108
+ this . agentWatchers [ agentId ] . dispose ( )
98
109
}
99
110
} )
100
111
}
0 commit comments