-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathConnectStep.kt
More file actions
136 lines (123 loc) · 4.9 KB
/
Copy pathConnectStep.kt
File metadata and controls
136 lines (123 loc) · 4.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package com.coder.toolbox.views
import com.coder.toolbox.CoderToolboxContext
import com.coder.toolbox.cli.CoderCLIManager
import com.coder.toolbox.cli.ensureCLI
import com.coder.toolbox.plugin.PluginManager
import com.coder.toolbox.sdk.CoderRestClient
import com.coder.toolbox.views.state.AuthContext
import com.coder.toolbox.views.state.AuthWizardState
import com.jetbrains.toolbox.api.localization.LocalizableString
import com.jetbrains.toolbox.api.ui.components.LabelField
import com.jetbrains.toolbox.api.ui.components.RowGroup
import com.jetbrains.toolbox.api.ui.components.ValidationErrorField
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
import java.util.concurrent.CancellationException
private const val USER_HIT_THE_BACK_BUTTON = "User hit the back button"
/**
* A page that connects a REST client and cli to Coder.
*/
class ConnectStep(
private val context: CoderToolboxContext,
private val shouldAutoLogin: StateFlow<Boolean>,
private val notify: (String, Throwable) -> Unit,
private val refreshWizard: () -> Unit,
private val onConnect: suspend (
client: CoderRestClient,
cli: CoderCLIManager,
) -> Unit,
) : WizardStep {
private var signInJob: Job? = null
private val statusField = LabelField(context.i18n.pnotr(""))
private val errorField = ValidationErrorField(context.i18n.pnotr(""))
override val panel: RowGroup = RowGroup(
RowGroup.RowField(statusField),
RowGroup.RowField(errorField)
)
override val nextButtonTitle: LocalizableString? = null
override fun onVisible() {
errorField.textState.update {
context.i18n.pnotr("")
}
if (AuthContext.isNotReadyForAuth()) {
errorField.textState.update {
context.i18n.pnotr("URL and token were not properly configured. Please go back and provide a proper URL and token!")
}
return
}
statusField.textState.update { context.i18n.pnotr("Connecting to ${AuthContext.url!!.host}...") }
connect()
}
/**
* Try connecting to Coder with the provided URL and token.
*/
private fun connect() {
if (!AuthContext.hasUrl()) {
errorField.textState.update { context.i18n.ptrl("URL is required") }
return
}
if (!AuthContext.hasToken()) {
errorField.textState.update { context.i18n.ptrl("Token is required") }
return
}
signInJob?.cancel()
signInJob = context.cs.launch {
try {
statusField.textState.update { (context.i18n.ptrl("Authenticating to ${AuthContext.url!!.host}...")) }
val client = CoderRestClient(
context,
AuthContext.url!!,
AuthContext.token!!,
PluginManager.pluginInfo.version,
)
// allows interleaving with the back/cancel action
yield()
client.authenticate()
statusField.textState.update { (context.i18n.ptrl("Checking Coder binary...")) }
val cli = ensureCLI(context, client.url, client.buildVersion)
// We only need to log in if we are using token-based auth.
if (client.token != null) {
statusField.textState.update { (context.i18n.ptrl("Configuring CLI...")) }
// allows interleaving with the back/cancel action
yield()
cli.login(client.token)
}
statusField.textState.update { (context.i18n.ptrl("Successfully configured ${AuthContext.url!!.host}...")) }
// allows interleaving with the back/cancel action
yield()
AuthContext.reset()
AuthWizardState.resetSteps()
onConnect(client, cli)
} catch (ex: CancellationException) {
if (ex.message != USER_HIT_THE_BACK_BUTTON) {
notify("Connection to ${AuthContext.url!!.host} was configured", ex)
onBack()
refreshWizard()
}
} catch (ex: Exception) {
notify("Failed to configure ${AuthContext.url!!.host}", ex)
onBack()
refreshWizard()
}
}
}
override fun onNext(): Boolean {
return false
}
override fun onBack() {
try {
signInJob?.cancel(CancellationException(USER_HIT_THE_BACK_BUTTON))
} finally {
if (shouldAutoLogin.value) {
AuthContext.reset()
AuthWizardState.resetSteps()
context.secrets.rememberMe = false
} else {
AuthWizardState.goToPreviousStep()
}
}
}
}