@@ -147,56 +147,24 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
147
147
override fun getRecentsTitle () = CoderGatewayBundle .message(" gateway.connector.title" )
148
148
149
149
override fun updateRecentView () {
150
- triggerWorkspacePolling()
150
+ // Render immediately so we can display spinners for each connection
151
+ // that we have not fetched a workspace for yet.
151
152
updateContentView()
153
+ // After each poll, the content view will be updated again.
154
+ triggerWorkspacePolling()
152
155
}
153
156
157
+ /* *
158
+ * Render the most recent connections, matching with fetched workspaces.
159
+ */
154
160
private fun updateContentView () {
155
- val connectionsByDeployment = recentConnectionsService.getAllRecentConnections()
156
- // Validate and parse connections.
157
- .mapNotNull {
158
- try {
159
- it.toWorkspaceProjectIDE()
160
- } catch (e: Exception ) {
161
- logger.warn(" Removing invalid recent connection $it " , e)
162
- recentConnectionsService.removeConnection(it)
163
- null
164
- }
165
- }
166
- // Filter by the search.
167
- .filter { matchesFilter(it) }
168
- // Group by the deployment.
169
- .groupBy { it.deploymentURL }
170
- // Group the connections in each deployment by workspace.
171
- .mapValues { (deploymentURL, deploymentConnections) ->
172
- deploymentConnections
173
- .groupBy { it.name.split(" ." , limit = 2 ).first() }
174
- // Find the matching workspace in the query response.
175
- .mapValues { (workspaceName, connections) ->
176
- val deployment = deployments[deploymentURL]
177
- val workspaceWithAgent = deployment?.items?.firstOrNull { it.workspace.name == workspaceName }
178
- Pair (workspaceWithAgent, connections)
179
- }
180
- // Remove connections to workspaces that no longer exist.
181
- .filter {
182
- val (workspaceWithAgent, workspaceConnections) = it.value
183
- if (workspaceWithAgent == null && deployments[deploymentURL]?.didFetch() == true ) {
184
- logger.info(" Removing recent connections for deleted workspace ${it.key} (found ${workspaceConnections.size} )" )
185
- workspaceConnections.forEach { conn ->
186
- recentConnectionsService.removeConnection(conn.toRecentWorkspaceConnection())
187
- }
188
- false
189
- } else {
190
- true
191
- }
192
- }
193
- }
161
+ val connections = getConnectionsByDeployment(true )
194
162
recentWorkspacesContentPanel.viewport.view = panel {
195
- connectionsByDeployment .forEach { (deploymentURL, connectionsByWorkspace) ->
163
+ connections .forEach { (deploymentURL, connectionsByWorkspace) ->
196
164
val deployment = deployments[deploymentURL]
197
165
val deploymentError = deployment?.error
198
- connectionsByWorkspace.forEach { (workspaceName, value ) ->
199
- val ( workspaceWithAgent, connections) = value
166
+ connectionsByWorkspace.forEach { (workspaceName, connections ) ->
167
+ val workspaceWithAgent = deployment?.items?.firstOrNull { it.workspace.name == workspaceName }
200
168
val status = if (deploymentError != null ) {
201
169
Triple (UIUtil .getBalloonErrorIcon(), UIUtil .getErrorForeground(), deploymentError)
202
170
} else if (workspaceWithAgent != null ) {
@@ -280,6 +248,31 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
280
248
}
281
249
}
282
250
251
+ /* *
252
+ * Get valid connections grouped by deployment and workspace.
253
+ */
254
+ private fun getConnectionsByDeployment (filter : Boolean ): Map <String , Map <String , List <WorkspaceProjectIDE >>> {
255
+ return recentConnectionsService.getAllRecentConnections()
256
+ // Validate and parse connections.
257
+ .mapNotNull {
258
+ try {
259
+ it.toWorkspaceProjectIDE()
260
+ } catch (e: Exception ) {
261
+ logger.warn(" Removing invalid recent connection $it " , e)
262
+ recentConnectionsService.removeConnection(it)
263
+ null
264
+ }
265
+ }
266
+ .filter { ! filter || matchesFilter(it) }
267
+ // Group by the deployment.
268
+ .groupBy { it.deploymentURL }
269
+ // Group the connections in each deployment by workspace.
270
+ .mapValues { (_, connections) ->
271
+ connections
272
+ .groupBy { it.name.split(" ." , limit = 2 ).first() }
273
+ }
274
+ }
275
+
283
276
/* *
284
277
* Return true if the connection matches the current filter.
285
278
*/
@@ -319,35 +312,40 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
319
312
*/
320
313
private suspend fun fetchWorkspaces () {
321
314
withContext(Dispatchers .IO ) {
322
- recentConnectionsService.getAllRecentConnections()
323
- .mapNotNull { it.deploymentURL }
324
- .toSet()
325
- .map { Pair (it, deployments.getOrPut(it) { DeploymentInfo () }) }
326
- .forEach { (deploymentURL, deployment) ->
327
- val client = deployment.client ? : try {
328
- val cli = CoderCLIManager (deploymentURL.toURL())
329
- val (_, token) = settings.readConfig(cli.coderConfigPath)
330
- deployment.client = CoderRestClientService (deploymentURL.toURL(), token)
315
+ val connections = getConnectionsByDeployment(false )
316
+ .map { Triple (it.key, deployments.getOrPut(it.key) { DeploymentInfo () }, it.value) }
317
+ connections.forEach { (deploymentURL, deployment, workspaces) ->
318
+ val client = deployment.client ? : try {
319
+ val cli = CoderCLIManager (deploymentURL.toURL())
320
+ val (_, token) = settings.readConfig(cli.coderConfigPath)
321
+ deployment.client = CoderRestClientService (deploymentURL.toURL(), token)
322
+ deployment.error = null
323
+ deployment.client
324
+ } catch (e: Exception ) {
325
+ logger.error(" Unable to create client for $deploymentURL " , e)
326
+ deployment.error = " Error connecting to $deploymentURL : ${e.message} "
327
+ null
328
+ }
329
+ if (client != null ) {
330
+ try {
331
+ val items = client.workspaces().flatMap { it.toAgentList() }
332
+ deployment.items = items
331
333
deployment.error = null
332
- deployment.client
333
- } catch (e: Exception ) {
334
- logger.error(" Unable to create client for $deploymentURL " , e)
335
- deployment.error = " Error connecting to $deploymentURL : ${e.message} "
336
- null
337
- }
338
- if (client != null ) {
339
- try {
340
- deployment.items = client.workspaces()
341
- .flatMap { it.toAgentList() }
342
- deployment.error = null
343
- } catch (e: Exception ) {
344
- // TODO: If this is an auth error, ask for a new token.
345
- logger.error(" Failed to fetch workspaces from ${client.url} " , e)
346
- deployment.items = null
347
- deployment.error = e.message ? : " Request failed without further details"
334
+ // Delete connections that have no workspace.
335
+ workspaces.forEach { name, connections ->
336
+ if (items.firstOrNull { it.workspace.name == name } == null ) {
337
+ logger.info(" Removing recent connections for deleted workspace ${name} (found ${connections.size} )" )
338
+ connections.forEach { recentConnectionsService.removeConnection(it.toRecentWorkspaceConnection()) }
339
+ }
348
340
}
341
+ } catch (e: Exception ) {
342
+ // TODO: If this is an auth error, ask for a new token.
343
+ logger.error(" Failed to fetch workspaces from ${client.url} " , e)
344
+ deployment.items = null
345
+ deployment.error = e.message ? : " Request failed without further details"
349
346
}
350
347
}
348
+ }
351
349
}
352
350
withContext(Dispatchers .Main ) {
353
351
updateContentView()
0 commit comments