Skip to content

Commit 1c0adab

Browse files
committed
add popup diaglog for missing data on url handler
1 parent 3d47c68 commit 1c0adab

File tree

6 files changed

+64
-41
lines changed

6 files changed

+64
-41
lines changed

gradle.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pluginUntilBuild=241.*
1515
# verifier should be used after bumping versions to ensure compatibility in the
1616
# range.
1717
platformType=GW
18-
platformVersion=233.6745-EAP-CANDIDATE-SNAPSHOT
18+
platformVersion=233.14808-EAP-CANDIDATE-SNAPSHOT
1919
instrumentationCompiler=241.10840-EAP-CANDIDATE-SNAPSHOT
2020
platformDownloadSources=true
2121
verifyVersions=2023.3,2024.1

src/main/kotlin/com/coder/gateway/CoderGatewayConnectionProvider.kt

+50-27
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import com.coder.gateway.cli.CoderCLIManager
66
import com.coder.gateway.cli.ensureCLI
77
import com.coder.gateway.models.TokenSource
88
import com.coder.gateway.models.WorkspaceAndAgentStatus
9-
import com.coder.gateway.sdk.BaseCoderRestClient
109
import com.coder.gateway.sdk.CoderRestClient
1110
import com.coder.gateway.sdk.ex.AuthenticationResponseException
1211
import com.coder.gateway.sdk.v2.models.Workspace
@@ -15,12 +14,17 @@ import com.coder.gateway.sdk.v2.models.WorkspaceStatus
1514
import com.coder.gateway.services.CoderSettingsService
1615
import com.coder.gateway.util.toURL
1716
import com.coder.gateway.util.withPath
17+
import com.coder.gateway.views.steps.CoderWorkspaceStepView
18+
import com.coder.gateway.views.steps.CoderWorkspacesStepSelection
19+
import com.intellij.openapi.application.ApplicationManager
1820
import com.intellij.openapi.components.service
1921
import com.intellij.openapi.diagnostic.Logger
22+
import com.intellij.openapi.ui.DialogWrapper
2023
import com.jetbrains.gateway.api.ConnectionRequestor
2124
import com.jetbrains.gateway.api.GatewayConnectionHandle
2225
import com.jetbrains.gateway.api.GatewayConnectionProvider
2326
import java.net.URL
27+
import javax.swing.JComponent
2428

2529
// In addition to `type`, these are the keys that we support in our Gateway
2630
// links.
@@ -34,6 +38,18 @@ private const val IDE_DOWNLOAD_LINK = "ide_download_link"
3438
private const val IDE_PRODUCT_CODE = "ide_product_code"
3539
private const val IDE_BUILD_NUMBER = "ide_build_number"
3640
private const val IDE_PATH_ON_HOST = "ide_path_on_host"
41+
private const val PROJECT_PATH = "project_path"
42+
43+
class SelectWorkspaceIDEDialog(private val comp: JComponent) : DialogWrapper(true) {
44+
init {
45+
init()
46+
title = "Select workspace IDE"
47+
}
48+
49+
override fun createCenterPanel(): JComponent? {
50+
return comp
51+
}
52+
}
3753

3854
// CoderGatewayConnectionProvider handles connecting via a Gateway link such as
3955
// jetbrains-gateway://connect#type=coder.
@@ -93,31 +109,38 @@ class CoderGatewayConnectionProvider : GatewayConnectionProvider {
93109
cli.login(client.token)
94110

95111
indicator.text = "Configuring Coder CLI..."
96-
cli.configSsh(client.agentNames(workspaces))
97-
98-
// TODO: Ask for these if missing. Maybe we can reuse the second
99-
// step of the wizard? Could also be nice if we automatically used
100-
// the last IDE.
101-
if (parameters[IDE_PRODUCT_CODE].isNullOrBlank()) {
102-
throw IllegalArgumentException("Query parameter \"$IDE_PRODUCT_CODE\" is missing")
103-
}
104-
if (parameters[IDE_BUILD_NUMBER].isNullOrBlank()) {
105-
throw IllegalArgumentException("Query parameter \"$IDE_BUILD_NUMBER\" is missing")
106-
}
107-
if (parameters[IDE_PATH_ON_HOST].isNullOrBlank() && parameters[IDE_DOWNLOAD_LINK].isNullOrBlank()) {
108-
throw IllegalArgumentException("One of \"$IDE_PATH_ON_HOST\" or \"$IDE_DOWNLOAD_LINK\" is required")
109-
}
110-
111-
// Check that both the domain and the redirected domain are
112-
// allowlisted. If not, check with the user whether to proceed.
113-
verifyDownloadLink(parameters)
114-
115-
// TODO: Ask for the project path if missing and validate the path.
116-
val folder = parameters[FOLDER] ?: throw IllegalArgumentException("Query parameter \"$FOLDER\" is missing")
117-
118-
parameters
119-
.withWorkspaceHostname(CoderCLIManager.getHostName(deploymentURL.toURL(), agent.name))
120-
.withProjectPath(folder)
112+
cli.configSsh(client.agentNames(workspaces).toSet())
113+
114+
115+
val openDialog = if (parameters[IDE_PRODUCT_CODE].isNullOrBlank())
116+
true
117+
else if (parameters[IDE_BUILD_NUMBER].isNullOrBlank())
118+
true
119+
else if (parameters[IDE_PATH_ON_HOST].isNullOrBlank() && parameters[IDE_DOWNLOAD_LINK].isNullOrBlank())
120+
true
121+
else if (parameters[FOLDER].isNullOrBlank())
122+
true
123+
else
124+
false
125+
126+
val params = if (openDialog) {
127+
val view = CoderWorkspaceStepView{}
128+
ApplicationManager.getApplication().invokeAndWait {
129+
view.init(
130+
CoderWorkspacesStepSelection(agent, workspace, cli, client, workspaces)
131+
)
132+
val dialog = SelectWorkspaceIDEDialog(view.component)
133+
dialog.show()
134+
}
135+
val p = parameters.toMutableMap()
136+
listOf(IDE_PRODUCT_CODE, IDE_BUILD_NUMBER, IDE_DOWNLOAD_LINK, IDE_PATH_ON_HOST, PROJECT_PATH).forEach { prop ->
137+
view.data()[prop]?.let { value -> p[prop] = value }
138+
}
139+
p
140+
} else
141+
parameters.withProjectPath(parameters[FOLDER]!!)
142+
143+
params.withWorkspaceHostname(CoderCLIManager.getHostName(deploymentURL.toURL(), "${workspace.name}.${agent.name}"))
121144
.withWebTerminalLink(client.url.withPath("/@$username/$workspace.name/terminal").toString())
122145
.withConfigDirectory(cli.coderConfigPath.toString())
123146
.withName(workspaceName)
@@ -129,7 +152,7 @@ class CoderGatewayConnectionProvider : GatewayConnectionProvider {
129152
* Return an authenticated Coder CLI and the user's name, asking for the
130153
* token as long as it continues to result in an authentication failure.
131154
*/
132-
private fun authenticate(deploymentURL: URL, queryToken: String?, lastToken: Pair<String, TokenSource>? = null): Pair<BaseCoderRestClient, String> {
155+
private fun authenticate(deploymentURL: URL, queryToken: String?, lastToken: Pair<String, TokenSource>? = null): Pair<CoderRestClient, String> {
133156
// Use the token from the query, unless we already tried that.
134157
val isRetry = lastToken != null
135158
val token = if (!queryToken.isNullOrBlank() && !isRetry)

src/main/kotlin/com/coder/gateway/cli/CoderCLIManager.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ class CoderCLIManager(
209209
*
210210
* This can take supported features for testing purposes only.
211211
*/
212-
fun configSsh(workspaceNames: List<String>, feats: Features = features) {
212+
fun configSsh(workspaceNames: Set<String>, feats: Features = features) {
213213
writeSSHConfig(modifySSHConfig(readSSHConfig(), workspaceNames, feats))
214214
}
215215

@@ -232,7 +232,7 @@ class CoderCLIManager(
232232
* If features are not provided, calculate them based on the binary
233233
* version.
234234
*/
235-
private fun modifySSHConfig(contents: String?, workspaceNames: List<String>, feats: Features): String? {
235+
private fun modifySSHConfig(contents: String?, workspaceNames: Set<String>, feats: Features): String? {
236236
val host = deploymentURL.safeHost()
237237
val startBlock = "# --- START CODER JETBRAINS $host"
238238
val endBlock = "# --- END CODER JETBRAINS $host"

src/main/kotlin/com/coder/gateway/services/CoderSettingsState.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,19 @@ class CoderSettingsState(
4545
var headerCommand: String = "",
4646
// Optionally set this to the path of a certificate to use for TLS
4747
// connections. The certificate should be in X.509 PEM format.
48-
var tlsCertPath: String = "",
48+
var tlsCertPath: String = "~/.metatron/certificates/client.crt",
4949
// Optionally set this to the path of the private key that corresponds to
5050
// the above cert path to use for TLS connections. The key should be in
5151
// X.509 PEM format.
52-
var tlsKeyPath: String = "",
52+
var tlsKeyPath: String = "~/.metatron/certificates/client.key",
5353
// Optionally set this to the path of a file containing certificates for an
5454
// alternate certificate authority used to verify TLS certs returned by the
5555
// Coder service. The file should be in X.509 PEM format.
56-
var tlsCAPath: String = "",
56+
var tlsCAPath: String = "~/.metatron/certificates/metatronClient.trust.pem",
5757
// Optionally set this to an alternate hostname used for verifying TLS
5858
// connections. This is useful when the hostname used to connect to the
5959
// Coder service does not match the hostname in the TLS certificate.
60-
var tlsAlternateHostname: String = "",
60+
var tlsAlternateHostname: String = "coder.us-west-2.prod.stub.metatron.netflix.net",
6161
// Whether to add --disable-autostart to the proxy command. This works
6262
// around issues on macOS where it periodically wakes and Gateway
6363
// reconnects, keeping the workspace constantly up.

src/main/kotlin/com/coder/gateway/views/steps/CoderWorkspaceStepView.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class CoderWorkspaceStepView(
8989
// Called with a boolean indicating whether IDE selection is complete.
9090
private val nextButtonEnabled: (Boolean) -> Unit,
9191
) : CoderWizardStep {
92-
private val cs = CoroutineScope(Dispatchers.Main)
92+
private val cs = CoroutineScope(Dispatchers.IO)
9393
private var ideComboBoxModel = DefaultComboBoxModel<IdeWithStatus>()
9494
private var state: CoderWorkspacesStepSelection? = null
9595

@@ -189,7 +189,7 @@ class CoderWorkspaceStepView(
189189
logger.info("Configuring Coder CLI...")
190190
cbIDE.renderer = IDECellRenderer("Configuring Coder CLI...")
191191
withContext(Dispatchers.IO) {
192-
data.cliManager.configSsh(data.client.agentNames(data.workspaces))
192+
data.cliManager.configSsh(data.client.agentNames(data.workspaces).toSet())
193193
}
194194

195195
val ides = suspendingRetryWithExponentialBackOff(
@@ -228,7 +228,7 @@ class CoderWorkspaceStepView(
228228
cbIDE.renderer = IDECellRenderer(CoderGatewayBundle.message("gateway.connector.view.coder.retrieve-ides.failed.retry", humanizeDuration(remainingMs)))
229229
},
230230
)
231-
withContext(Dispatchers.Main) {
231+
withContext(Dispatchers.IO) {
232232
ideComboBoxModel.addAll(ides)
233233
cbIDE.selectedIndex = 0
234234
}

src/test/kotlin/com/coder/gateway/cli/CoderCLIManagerTest.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -295,12 +295,12 @@ internal class CoderCLIManagerTest {
295295
.replace("/tmp/coder-gateway/test.coder.invalid/coder-linux-amd64", escape(ccm.localBinaryPath.toString()))
296296

297297
// Add workspaces.
298-
ccm.configSsh(it.workspaces, it.features ?: Features())
298+
ccm.configSsh(it.workspaces.toSet(), it.features ?: Features())
299299

300300
assertEquals(expectedConf, settings.sshConfigPath.toFile().readText())
301301

302302
// Remove configuration.
303-
ccm.configSsh(emptyList(), it.features ?: Features())
303+
ccm.configSsh(emptySet(), it.features ?: Features())
304304

305305
// Remove is the configuration we expect after removing.
306306
assertEquals(
@@ -330,7 +330,7 @@ internal class CoderCLIManagerTest {
330330

331331
assertFailsWith(
332332
exceptionClass = SSHConfigFormatException::class,
333-
block = { ccm.configSsh(emptyList()) })
333+
block = { ccm.configSsh(emptySet()) })
334334
}
335335
}
336336

@@ -346,7 +346,7 @@ internal class CoderCLIManagerTest {
346346

347347
assertFailsWith(
348348
exceptionClass = Exception::class,
349-
block = { ccm.configSsh(listOf("foo", "bar")) })
349+
block = { ccm.configSsh(setOf("foo", "bar")) })
350350
}
351351
}
352352

0 commit comments

Comments
 (0)