Skip to content

Commit 7bbc161

Browse files
committed
chore: refactor the error reporter
1 parent 837abb3 commit 7bbc161

File tree

4 files changed

+94
-49
lines changed

4 files changed

+94
-49
lines changed

src/main/kotlin/com/coder/toolbox/views/CoderCliSetupWizardPage.kt

Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,19 @@ package com.coder.toolbox.views
33
import com.coder.toolbox.CoderToolboxContext
44
import com.coder.toolbox.cli.CoderCLIManager
55
import com.coder.toolbox.sdk.CoderRestClient
6-
import com.coder.toolbox.sdk.ex.APIResponseException
76
import com.coder.toolbox.views.state.CoderCliSetupWizardState
87
import com.coder.toolbox.views.state.WizardStep
98
import com.jetbrains.toolbox.api.remoteDev.ProviderVisibilityState
109
import com.jetbrains.toolbox.api.ui.actions.RunnableActionDescription
1110
import com.jetbrains.toolbox.api.ui.components.UiField
12-
import kotlinx.coroutines.CoroutineName
1311
import kotlinx.coroutines.flow.MutableStateFlow
12+
import kotlinx.coroutines.flow.StateFlow
1413
import kotlinx.coroutines.flow.update
15-
import kotlinx.coroutines.launch
16-
import java.util.UUID
1714

1815
class CoderCliSetupWizardPage(
1916
private val context: CoderToolboxContext,
2017
private val settingsPage: CoderSettingsPage,
21-
private val visibilityState: MutableStateFlow<ProviderVisibilityState>,
18+
visibilityState: StateFlow<ProviderVisibilityState>,
2219
initialAutoSetup: Boolean = false,
2320
jumpToMainPageOnError: Boolean = false,
2421
onConnect: suspend (
@@ -31,33 +28,28 @@ class CoderCliSetupWizardPage(
3128
context.ui.showUiPage(settingsPage)
3229
})
3330

34-
private val deploymentUrlStep = DeploymentUrlStep(context, this::notify)
31+
private val deploymentUrlStep = DeploymentUrlStep(context, visibilityState)
3532
private val tokenStep = TokenStep(context)
3633
private val connectStep = ConnectStep(
3734
context,
3835
shouldAutoLogin = shouldAutoSetup,
3936
jumpToMainPageOnError,
40-
this::notify,
37+
visibilityState,
4138
this::displaySteps,
4239
onConnect
4340
)
41+
private val errorReporter = ErrorReporter.create(context, visibilityState, this.javaClass)
4442

4543
/**
4644
* Fields for this page, displayed in order.
4745
*/
4846
override val fields: MutableStateFlow<List<UiField>> = MutableStateFlow(emptyList())
4947
override val actionButtons: MutableStateFlow<List<RunnableActionDescription>> = MutableStateFlow(emptyList())
5048

51-
private val errorBuffer = mutableListOf<Throwable>()
5249

5350
override fun beforeShow() {
5451
displaySteps()
55-
if (errorBuffer.isNotEmpty() && visibilityState.value.applicationVisible) {
56-
errorBuffer.forEach {
57-
showError(it)
58-
}
59-
errorBuffer.clear()
60-
}
52+
errorReporter.flush()
6153
}
6254

6355
private fun displaySteps() {
@@ -124,30 +116,5 @@ class CoderCliSetupWizardPage(
124116
/**
125117
* Show an error as a popup on this page.
126118
*/
127-
fun notify(logPrefix: String, ex: Throwable) {
128-
context.logger.error(ex, logPrefix)
129-
if (!visibilityState.value.applicationVisible) {
130-
context.logger.debug("Toolbox is not yet visible, scheduling error to be displayed later")
131-
errorBuffer.add(ex)
132-
return
133-
}
134-
showError(ex)
135-
}
136-
137-
private fun showError(ex: Throwable) {
138-
val textError = if (ex is APIResponseException) {
139-
if (!ex.reason.isNullOrBlank()) {
140-
ex.reason
141-
} else ex.message
142-
} else ex.message
143-
144-
context.cs.launch(CoroutineName("Coder Setup Visual Error Reporting")) {
145-
context.ui.showSnackbar(
146-
UUID.randomUUID().toString(),
147-
context.i18n.ptrl("Error encountered while setting up Coder"),
148-
context.i18n.pnotr(textError ?: ""),
149-
context.i18n.ptrl("Dismiss")
150-
)
151-
}
152-
}
119+
fun notify(message: String, ex: Throwable) = errorReporter.report(message, ex)
153120
}

src/main/kotlin/com/coder/toolbox/views/ConnectStep.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.coder.toolbox.plugin.PluginManager
77
import com.coder.toolbox.sdk.CoderRestClient
88
import com.coder.toolbox.views.state.CoderCliSetupContext
99
import com.coder.toolbox.views.state.CoderCliSetupWizardState
10+
import com.jetbrains.toolbox.api.remoteDev.ProviderVisibilityState
1011
import com.jetbrains.toolbox.api.ui.components.LabelField
1112
import com.jetbrains.toolbox.api.ui.components.RowGroup
1213
import com.jetbrains.toolbox.api.ui.components.ValidationErrorField
@@ -27,24 +28,23 @@ class ConnectStep(
2728
private val context: CoderToolboxContext,
2829
private val shouldAutoLogin: StateFlow<Boolean>,
2930
private val jumpToMainPageOnError: Boolean,
30-
private val notify: (String, Throwable) -> Unit,
31+
visibilityState: StateFlow<ProviderVisibilityState>,
3132
private val refreshWizard: () -> Unit,
32-
private val onConnect: suspend (
33-
client: CoderRestClient,
34-
cli: CoderCLIManager,
35-
) -> Unit,
33+
private val onConnect: suspend (client: CoderRestClient, cli: CoderCLIManager) -> Unit,
3634
) : WizardStep {
3735
private var signInJob: Job? = null
3836

3937
private val statusField = LabelField(context.i18n.pnotr(""))
4038
private val errorField = ValidationErrorField(context.i18n.pnotr(""))
39+
private val errorReporter = ErrorReporter.create(context, visibilityState, this.javaClass)
4140

4241
override val panel: RowGroup = RowGroup(
4342
RowGroup.RowField(statusField),
4443
RowGroup.RowField(errorField)
4544
)
4645

4746
override fun onVisible() {
47+
errorReporter.flush()
4848
errorField.textState.update {
4949
context.i18n.pnotr("")
5050
}
@@ -114,12 +114,12 @@ class ConnectStep(
114114
context.envPageManager.showPluginEnvironmentsPage()
115115
} catch (ex: CancellationException) {
116116
if (ex.message != USER_HIT_THE_BACK_BUTTON) {
117-
notify("Connection to $hostName was configured", ex)
117+
errorReporter.report("Connection to $hostName was configured", ex)
118118
handleNavigation()
119119
refreshWizard()
120120
}
121121
} catch (ex: Exception) {
122-
notify("Failed to configure $hostName", ex)
122+
errorReporter.report("Failed to configure $hostName", ex)
123123
handleNavigation()
124124
refreshWizard()
125125
}

src/main/kotlin/com/coder/toolbox/views/DeploymentUrlStep.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import com.coder.toolbox.util.toURL
66
import com.coder.toolbox.util.validateStrictWebUrl
77
import com.coder.toolbox.views.state.CoderCliSetupContext
88
import com.coder.toolbox.views.state.CoderCliSetupWizardState
9+
import com.jetbrains.toolbox.api.remoteDev.ProviderVisibilityState
910
import com.jetbrains.toolbox.api.ui.components.CheckboxField
1011
import com.jetbrains.toolbox.api.ui.components.LabelField
1112
import com.jetbrains.toolbox.api.ui.components.LabelStyleType
1213
import com.jetbrains.toolbox.api.ui.components.RowGroup
1314
import com.jetbrains.toolbox.api.ui.components.TextField
1415
import com.jetbrains.toolbox.api.ui.components.TextType
1516
import com.jetbrains.toolbox.api.ui.components.ValidationErrorField
17+
import kotlinx.coroutines.flow.StateFlow
1618
import kotlinx.coroutines.flow.update
1719
import java.net.MalformedURLException
1820
import java.net.URL
@@ -25,9 +27,11 @@ import java.net.URL
2527
*/
2628
class DeploymentUrlStep(
2729
private val context: CoderToolboxContext,
28-
private val notify: (String, Throwable) -> Unit
30+
visibilityState: StateFlow<ProviderVisibilityState>,
2931
) :
3032
WizardStep {
33+
private val errorReporter = ErrorReporter.create(context, visibilityState, this.javaClass)
34+
3135
private val urlField = TextField(context.i18n.ptrl("Deployment URL"), "", TextType.General)
3236
private val emptyLine = LabelField(context.i18n.pnotr(""), LabelStyleType.Normal)
3337

@@ -66,6 +70,7 @@ class DeploymentUrlStep(
6670
signatureFallbackStrategyField.checkedState.update {
6771
context.settingsStore.fallbackOnCoderForSignatures.isAllowed()
6872
}
73+
errorReporter.flush()
6974
}
7075

7176
override fun onNext(): Boolean {
@@ -78,7 +83,7 @@ class DeploymentUrlStep(
7883
try {
7984
CoderCliSetupContext.url = validateRawUrl(url)
8085
} catch (e: MalformedURLException) {
81-
notify("URL is invalid", e)
86+
errorReporter.report("URL is invalid", e)
8287
return false
8388
}
8489
if (context.settingsStore.requireTokenAuth) {
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.coder.toolbox.views
2+
3+
import com.coder.toolbox.CoderToolboxContext
4+
import com.coder.toolbox.sdk.ex.APIResponseException
5+
import com.jetbrains.toolbox.api.remoteDev.ProviderVisibilityState
6+
import kotlinx.coroutines.flow.StateFlow
7+
import kotlinx.coroutines.launch
8+
import java.util.UUID
9+
10+
sealed class ErrorReporter {
11+
12+
/**
13+
* Logs and show errors as popups.
14+
*/
15+
abstract fun report(message: String, ex: Throwable)
16+
17+
/**
18+
* Processes any buffered errors when the application becomes visible.
19+
*/
20+
abstract fun flush()
21+
22+
companion object {
23+
fun create(
24+
context: CoderToolboxContext,
25+
visibilityState: StateFlow<ProviderVisibilityState>,
26+
callerClass: Class<*>
27+
): ErrorReporter = ErrorReporterImpl(context, visibilityState, callerClass)
28+
}
29+
}
30+
31+
private class ErrorReporterImpl(
32+
private val context: CoderToolboxContext,
33+
private val visibilityState: StateFlow<ProviderVisibilityState>,
34+
private val callerClass: Class<*>
35+
) : ErrorReporter() {
36+
private val errorBuffer = mutableListOf<Throwable>()
37+
38+
override fun report(message: String, ex: Throwable) {
39+
context.logger.error(ex, "[${callerClass.simpleName}] $message")
40+
if (!visibilityState.value.applicationVisible) {
41+
context.logger.debug("Toolbox is not yet visible, scheduling error to be displayed later")
42+
errorBuffer.add(ex)
43+
return
44+
}
45+
showError(ex)
46+
}
47+
48+
private fun showError(ex: Throwable) {
49+
val textError = if (ex is APIResponseException) {
50+
if (!ex.reason.isNullOrBlank()) {
51+
ex.reason
52+
} else ex.message
53+
} else ex.message ?: ex.toString()
54+
context.cs.launch {
55+
context.ui.showSnackbar(
56+
UUID.randomUUID().toString(),
57+
context.i18n.ptrl("Error encountered while setting up Coder"),
58+
context.i18n.pnotr(textError ?: ""),
59+
context.i18n.ptrl("Dismiss")
60+
)
61+
}
62+
}
63+
64+
65+
override fun flush() {
66+
if (errorBuffer.isNotEmpty() && visibilityState.value.applicationVisible) {
67+
errorBuffer.forEach {
68+
showError(it)
69+
}
70+
errorBuffer.clear()
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)