Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 41728d9

Browse files
authoredJun 24, 2022
Merge pull request #23 from coder/report-rest-client-errors
Report rest client errors, dispose coroutine scopes & disable last Next button - Catch&report errors raised by the REST client - the exceptions from the rest client were handled by the coroutine scope which canceled everything in the scope without any event logged to the user. - the exceptions are now catched inside the coroutine scope & logged. - any error logged through the diagnostic logger is also displayed in the Gateway UI through the internal errors message panel (red balloon in the bottom right corner) - Dispose the coroutine scope when is no longer needed - disable next action button in the last step of the wizard when no IDE could be resolved
2 parents 5ca0790 + 0714fdc commit 41728d9

8 files changed

+72
-27
lines changed
 

‎src/main/kotlin/com/coder/gateway/sdk/CoderRestClientService.kt

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package com.coder.gateway.sdk
22

33
import com.coder.gateway.sdk.convertors.InstantConverter
4-
import com.coder.gateway.sdk.ex.AuthenticationException
4+
import com.coder.gateway.sdk.ex.AuthenticationResponseException
5+
import com.coder.gateway.sdk.ex.WorkspaceResponseException
56
import com.coder.gateway.sdk.v2.CoderV2RestFacade
67
import com.coder.gateway.sdk.v2.models.BuildInfo
78
import com.coder.gateway.sdk.v2.models.User
@@ -30,7 +31,7 @@ class CoderRestClientService {
3031

3132
/**
3233
* This must be called before anything else. It will authenticate with coder and retrieve a session token
33-
* @throws [AuthenticationException] if authentication failed
34+
* @throws [AuthenticationResponseException] if authentication failed.
3435
*/
3536
fun initClientSession(url: URL, token: String): User {
3637
val cookieUrl = url.toHttpUrlOrNull()!!
@@ -61,7 +62,7 @@ class CoderRestClientService {
6162

6263
val userResponse = retroRestClient.me().execute()
6364
if (!userResponse.isSuccessful) {
64-
throw IllegalStateException("Could not retrieve information about logged use:${userResponse.code()}, reason: ${userResponse.message()}")
65+
throw AuthenticationResponseException("Could not retrieve information about logged user:${userResponse.code()}, reason: ${userResponse.message()}")
6566
}
6667

6768
coderURL = url
@@ -72,10 +73,14 @@ class CoderRestClientService {
7273
return me
7374
}
7475

76+
/**
77+
* Retrieves the available workspaces created by the user.
78+
* @throws WorkspaceResponseException if workspaces could not be retrieved.
79+
*/
7580
fun workspaces(): List<Workspace> {
7681
val workspacesResponse = retroRestClient.workspaces().execute()
7782
if (!workspacesResponse.isSuccessful) {
78-
throw IllegalStateException("Could not retrieve Coder Workspaces:${workspacesResponse.code()}, reason: ${workspacesResponse.message()}")
83+
throw WorkspaceResponseException("Could not retrieve Coder Workspaces:${workspacesResponse.code()}, reason: ${workspacesResponse.message()}")
7984
}
8085

8186
return workspacesResponse.body()!!

‎src/main/kotlin/com/coder/gateway/sdk/ex/AuthenticationException.kt

-5
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.coder.gateway.sdk.ex
2+
3+
import java.io.IOException
4+
5+
class AuthenticationResponseException(reason: String) : IOException(reason)
6+
7+
class WorkspaceResponseException(reason: String) : IOException(reason)

‎src/main/kotlin/com/coder/gateway/views/CoderGatewayConnectorWizardView.kt

+10-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ class CoderGatewayConnectorWizardView : BorderLayoutPanel(), Disposable {
3232

3333
registerStep(CoderAuthStepView { next() })
3434
registerStep(CoderWorkspacesStepView())
35-
registerStep(CoderLocateRemoteProjectStepView())
35+
registerStep(CoderLocateRemoteProjectStepView {
36+
nextButton.isVisible = false
37+
})
3638

3739
addToBottom(createBackComponent())
3840

@@ -64,9 +66,15 @@ class CoderGatewayConnectorWizardView : BorderLayoutPanel(), Disposable {
6466
nextButton.text = nextActionText
6567
previousButton.text = previousActionText
6668
}
69+
showNavigationButtons()
6770
}
6871
}
6972

73+
private fun showNavigationButtons() {
74+
nextButton.isVisible = true
75+
previousButton.isVisible = true
76+
}
77+
7078
private fun next() {
7179
if (!doNextCallback()) return
7280
if (currentStep + 1 < steps.size) {
@@ -81,6 +89,7 @@ class CoderGatewayConnectorWizardView : BorderLayoutPanel(), Disposable {
8189
nextButton.text = nextActionText
8290
previousButton.text = previousActionText
8391
}
92+
showNavigationButtons()
8493
}
8594
}
8695

‎src/main/kotlin/com/coder/gateway/views/CoderGatewayRecentWorkspaceConnectionsView.kt

+7-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.coder.gateway.icons.CoderIcons
77
import com.coder.gateway.models.RecentWorkspaceConnection
88
import com.coder.gateway.services.CoderRecentWorkspaceConnectionsService
99
import com.intellij.ide.BrowserUtil
10+
import com.intellij.openapi.Disposable
1011
import com.intellij.openapi.actionSystem.AnActionEvent
1112
import com.intellij.openapi.components.service
1213
import com.intellij.openapi.project.DumbAwareAction
@@ -30,12 +31,13 @@ import com.jetbrains.gateway.ssh.IntelliJPlatformProduct
3031
import com.jetbrains.rd.util.lifetime.Lifetime
3132
import kotlinx.coroutines.CoroutineScope
3233
import kotlinx.coroutines.Dispatchers
34+
import kotlinx.coroutines.cancel
3335
import kotlinx.coroutines.launch
3436
import java.awt.Dimension
3537
import javax.swing.JComponent
3638
import javax.swing.event.DocumentEvent
3739

38-
class CoderGatewayRecentWorkspaceConnectionsView : GatewayRecentConnections {
40+
class CoderGatewayRecentWorkspaceConnectionsView : GatewayRecentConnections, Disposable {
3941
private val recentConnectionsService = service<CoderRecentWorkspaceConnectionsService>()
4042
private val cs = CoroutineScope(Dispatchers.Main)
4143

@@ -137,4 +139,8 @@ class CoderGatewayRecentWorkspaceConnectionsView : GatewayRecentConnections {
137139
border = JBUI.Borders.empty(12, 0, 0, 12)
138140
}
139141
}
142+
143+
override fun dispose() {
144+
cs.cancel()
145+
}
140146
}

‎src/main/kotlin/com/coder/gateway/views/steps/CoderAuthStepView.kt

+7-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.coder.gateway.models.CoderWorkspacesWizardModel
88
import com.coder.gateway.sdk.CoderCLIManager
99
import com.coder.gateway.sdk.CoderRestClientService
1010
import com.coder.gateway.sdk.OS
11+
import com.coder.gateway.sdk.ex.AuthenticationResponseException
1112
import com.coder.gateway.sdk.getOS
1213
import com.coder.gateway.sdk.toURL
1314
import com.coder.gateway.sdk.withPath
@@ -89,7 +90,12 @@ class CoderAuthStepView(private val nextAction: () -> Unit) : CoderWorkspacesWiz
8990
if (pastedToken.isNullOrBlank()) {
9091
return false
9192
}
92-
coderClient.initClientSession(model.coderURL.toURL(), pastedToken)
93+
try {
94+
coderClient.initClientSession(model.coderURL.toURL(), pastedToken)
95+
} catch (e: AuthenticationResponseException) {
96+
logger.error("Could not authenticate on ${model.coderURL}. Reason $e")
97+
return false
98+
}
9399
model.token = pastedToken
94100
model.buildVersion = coderClient.buildVersion
95101

‎src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt

+20-14
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import com.jetbrains.gateway.ssh.IntelliJPlatformProduct
3131
import com.jetbrains.gateway.ssh.guessOs
3232
import kotlinx.coroutines.CoroutineScope
3333
import kotlinx.coroutines.Dispatchers
34+
import kotlinx.coroutines.cancel
3435
import kotlinx.coroutines.launch
3536
import kotlinx.coroutines.withContext
3637
import java.awt.Component
@@ -43,7 +44,7 @@ import javax.swing.JPanel
4344
import javax.swing.ListCellRenderer
4445
import javax.swing.SwingConstants
4546

46-
class CoderLocateRemoteProjectStepView : CoderWorkspacesWizardStep, Disposable {
47+
class CoderLocateRemoteProjectStepView(private val disableNextAction: () -> Unit) : CoderWorkspacesWizardStep, Disposable {
4748
private val cs = CoroutineScope(Dispatchers.Main)
4849
private val coderClient: CoderRestClientService = ApplicationManager.getApplication().getService(CoderRestClientService::class.java)
4950

@@ -110,14 +111,28 @@ class CoderLocateRemoteProjectStepView : CoderWorkspacesWizardStep, Disposable {
110111

111112
cs.launch {
112113
logger.info("Retrieving available IDE's for ${selectedWorkspace.name} workspace...")
113-
try {
114-
val workspaceOS = withContext(Dispatchers.IO) {
114+
val workspaceOS = withContext(Dispatchers.IO) {
115+
try {
115116
RemoteCredentialsHolder().apply {
116117
setHost("coder.${selectedWorkspace.name}")
117118
userName = "coder"
118119
authType = AuthType.OPEN_SSH
119120
}.guessOs
121+
} catch (e: Exception) {
122+
logger.error("Could not resolve any IDE for workspace ${selectedWorkspace.name}. Reason: $e")
123+
null
120124
}
125+
}
126+
if (workspaceOS == null) {
127+
disableNextAction()
128+
cbIDE.renderer = object : ColoredListCellRenderer<IdeWithStatus>() {
129+
override fun customizeCellRenderer(list: JList<out IdeWithStatus>, value: IdeWithStatus?, index: Int, isSelected: Boolean, cellHasFocus: Boolean) {
130+
background = UIUtil.getListBackground(isSelected, cellHasFocus)
131+
icon = UIUtil.getBalloonErrorIcon()
132+
append(CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.ide.error.text", selectedWorkspace.name))
133+
}
134+
}
135+
} else {
121136
logger.info("Resolved OS and Arch for ${selectedWorkspace.name} is: $workspaceOS")
122137
val idesWithStatus = IntelliJPlatformProduct.values()
123138
.filter { it.showInGateway }
@@ -130,15 +145,6 @@ class CoderLocateRemoteProjectStepView : CoderWorkspacesWizardStep, Disposable {
130145
ideComboBoxModel.addAll(idesWithStatus)
131146
cbIDE.selectedIndex = 0
132147
}
133-
} catch (e: Exception) {
134-
logger.error("Could not resolve any IDE for workspace ${selectedWorkspace.name}. Reason: $e")
135-
cbIDE.renderer = object : ColoredListCellRenderer<IdeWithStatus>() {
136-
override fun customizeCellRenderer(list: JList<out IdeWithStatus>, value: IdeWithStatus?, index: Int, isSelected: Boolean, cellHasFocus: Boolean) {
137-
background = UIUtil.getListBackground(isSelected, cellHasFocus)
138-
icon = UIUtil.getBalloonErrorIcon()
139-
append(CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.ide.error.text", selectedWorkspace.name))
140-
}
141-
}
142148
}
143149
}
144150
}
@@ -147,7 +153,6 @@ class CoderLocateRemoteProjectStepView : CoderWorkspacesWizardStep, Disposable {
147153
val selectedIDE = cbIDE.selectedItem ?: return false
148154

149155
cs.launch {
150-
151156
GatewayUI.getInstance().connect(
152157
mapOf(
153158
"type" to "coder",
@@ -164,6 +169,7 @@ class CoderLocateRemoteProjectStepView : CoderWorkspacesWizardStep, Disposable {
164169
}
165170

166171
override fun dispose() {
172+
cs.cancel()
167173
}
168174

169175
companion object {
@@ -203,7 +209,7 @@ class CoderLocateRemoteProjectStepView : CoderWorkspacesWizardStep, Disposable {
203209
background = UIUtil.getListBackground(isSelected, cellHasFocus)
204210
}
205211
} else {
206-
panel { }
212+
panel { }
207213
}
208214
}
209215
}

‎src/main/kotlin/com/coder/gateway/views/steps/CoderWorkspacesStepView.kt

+12-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.coder.gateway.sdk.v2.models.Workspace
88
import com.intellij.ide.IdeBundle
99
import com.intellij.openapi.Disposable
1010
import com.intellij.openapi.application.ApplicationManager
11+
import com.intellij.openapi.diagnostic.Logger
1112
import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeScreenUIManager
1213
import com.intellij.ui.CollectionListModel
1314
import com.intellij.ui.components.JBList
@@ -19,6 +20,7 @@ import com.intellij.ui.dsl.gridLayout.VerticalAlign
1920
import com.intellij.util.ui.JBFont
2021
import kotlinx.coroutines.CoroutineScope
2122
import kotlinx.coroutines.Dispatchers
23+
import kotlinx.coroutines.cancel
2224
import kotlinx.coroutines.launch
2325
import kotlinx.coroutines.withContext
2426

@@ -57,7 +59,12 @@ class CoderWorkspacesStepView : CoderWorkspacesWizardStep, Disposable {
5759

5860
cs.launch {
5961
val workspaceList = withContext(Dispatchers.IO) {
60-
coderClient.workspaces()
62+
try {
63+
coderClient.workspaces()
64+
} catch (e: Exception) {
65+
logger.error("Could not retrieve workspaces for ${coderClient.me.username} on ${coderClient.coderURL}. Reason: $e")
66+
emptyList()
67+
}
6168
}
6269
workspaceList.forEach {
6370
workspaces.add(it)
@@ -75,6 +82,10 @@ class CoderWorkspacesStepView : CoderWorkspacesWizardStep, Disposable {
7582
}
7683

7784
override fun dispose() {
85+
cs.cancel()
86+
}
7887

88+
companion object {
89+
val logger = Logger.getInstance(CoderWorkspacesStepView::class.java.simpleName)
7990
}
8091
}

0 commit comments

Comments
 (0)
Failed to load comments.