Skip to content

Commit c56250a

Browse files
authored
impl: support uri handling (#35)
- reacts to URIs in the form of : `jetbrains://gateway/com.coder.toolbox?url=https%3A%2F%2Fdev.coder.com&token=....&workspace=bobiverse-bill` - query parameters like `url`, `token` and `workspace` are mandatory. A fallback implementation is now provided where we ask for these parameters in case they were missing - support for handling workspace from a Coder deployment that is not yet configured in the Toolbox. - resolves #37
1 parent 2b18fe4 commit c56250a

File tree

13 files changed

+233
-244
lines changed

13 files changed

+233
-244
lines changed

src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ import com.coder.toolbox.services.CoderSecretsService
77
import com.coder.toolbox.services.CoderSettingsService
88
import com.coder.toolbox.settings.CoderSettings
99
import com.coder.toolbox.settings.Source
10+
import com.coder.toolbox.util.CoderProtocolHandler
1011
import com.coder.toolbox.util.DialogUi
11-
import com.coder.toolbox.util.LinkHandler
12-
import com.coder.toolbox.util.toQueryParameters
1312
import com.coder.toolbox.views.Action
1413
import com.coder.toolbox.views.CoderSettingsPage
1514
import com.coder.toolbox.views.ConnectPage
@@ -53,7 +52,6 @@ class CoderRemoteProvider(
5352
private val secrets: CoderSecretsService = CoderSecretsService(context.secretsStore)
5453
private val settingsPage: CoderSettingsPage = CoderSettingsPage(context, settingsService)
5554
private val dialogUi = DialogUi(context, settings)
56-
private val linkHandler = LinkHandler(context, settings, httpClient, dialogUi)
5755

5856
// The REST client, if we are signed in
5957
private var client: CoderRestClient? = null
@@ -65,7 +63,9 @@ class CoderRemoteProvider(
6563

6664
// On the first load, automatically log in if we can.
6765
private var firstRun = true
68-
66+
private val isInitialized: MutableStateFlow<Boolean> = MutableStateFlow(false)
67+
private var coderHeaderPage = NewEnvironmentPage(context, context.i18n.pnotr(getDeploymentURL()?.first ?: ""))
68+
private val linkHandler = CoderProtocolHandler(context, settings, httpClient, dialogUi, isInitialized)
6969
override val environments: MutableStateFlow<LoadableState<List<RemoteProviderEnvironment>>> = MutableStateFlow(
7070
LoadableState.Value(emptyList())
7171
)
@@ -122,6 +122,12 @@ class CoderRemoteProvider(
122122
environments.update {
123123
LoadableState.Value(resolvedEnvironments.toList())
124124
}
125+
if (isInitialized.value == false) {
126+
context.logger.info("Environments for ${client.url} are now initialized")
127+
isInitialized.update {
128+
true
129+
}
130+
}
125131

126132
lastEnvironments = resolvedEnvironments
127133
} catch (_: CancellationException) {
@@ -171,14 +177,14 @@ class CoderRemoteProvider(
171177
/**
172178
* Cancel polling and clear the client and environments.
173179
*
174-
* Called as part of our own logout but it is unclear where it is called by
175-
* Toolbox. Maybe on uninstall?
180+
* Also called as part of our own logout.
176181
*/
177182
override fun close() {
178183
pollJob?.cancel()
179-
client = null
184+
client?.close()
180185
lastEnvironments = null
181186
environments.value = LoadableState.Value(emptyList())
187+
isInitialized.update { false }
182188
}
183189

184190
override val svgIcon: SvgIcon =
@@ -213,8 +219,7 @@ class CoderRemoteProvider(
213219
* Just displays the deployment URL at the moment, but we could use this as
214220
* a form for creating new environments.
215221
*/
216-
override fun getNewEnvironmentUiPage(): UiPage =
217-
NewEnvironmentPage(context, context.i18n.pnotr(getDeploymentURL()?.first ?: ""))
222+
override fun getNewEnvironmentUiPage(): UiPage = coderHeaderPage
218223

219224
/**
220225
* We always show a list of environments.
@@ -233,11 +238,13 @@ class CoderRemoteProvider(
233238
* Handle incoming links (like from the dashboard).
234239
*/
235240
override suspend fun handleUri(uri: URI) {
236-
val params = uri.toQueryParameters()
237-
context.cs.launch {
238-
val name = linkHandler.handle(params)
239-
// TODO@JB: Now what? How do we actually connect this workspace?
240-
context.logger.debug("External request for $name: $uri")
241+
linkHandler.handle(uri, shouldDoAutoLogin()) { restClient, cli ->
242+
// stop polling and de-initialize resources
243+
close()
244+
// start initialization with the new settings
245+
this@CoderRemoteProvider.client = restClient
246+
coderHeaderPage = NewEnvironmentPage(context, context.i18n.pnotr(restClient.url.toString()))
247+
pollJob = poll(restClient, cli)
241248
}
242249
}
243250

@@ -263,7 +270,7 @@ class CoderRemoteProvider(
263270
// Show sign in page if we have not configured the client yet.
264271
if (client == null) {
265272
// When coming back to the application, authenticate immediately.
266-
val autologin = firstRun && secrets.rememberMe == "true"
273+
val autologin = shouldDoAutoLogin()
267274
var autologinEx: Exception? = null
268275
secrets.lastToken.let { lastToken ->
269276
secrets.lastDeploymentURL.let { lastDeploymentURL ->
@@ -302,6 +309,8 @@ class CoderRemoteProvider(
302309
return null
303310
}
304311

312+
private fun shouldDoAutoLogin(): Boolean = firstRun && secrets.rememberMe == "true"
313+
305314
/**
306315
* Create a connect page that starts polling and resets the UI on success.
307316
*/

src/main/kotlin/com/coder/toolbox/CoderToolboxContext.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.jetbrains.toolbox.api.core.PluginSecretStore
44
import com.jetbrains.toolbox.api.core.PluginSettingsStore
55
import com.jetbrains.toolbox.api.core.diagnostics.Logger
66
import com.jetbrains.toolbox.api.localization.LocalizableStringFactory
7+
import com.jetbrains.toolbox.api.remoteDev.connection.ClientHelper
78
import com.jetbrains.toolbox.api.remoteDev.states.EnvironmentStateColorPalette
89
import com.jetbrains.toolbox.api.remoteDev.ui.EnvironmentUiPageManager
910
import com.jetbrains.toolbox.api.ui.ToolboxUi
@@ -13,6 +14,7 @@ data class CoderToolboxContext(
1314
val ui: ToolboxUi,
1415
val envPageManager: EnvironmentUiPageManager,
1516
val envStateColorPalette: EnvironmentStateColorPalette,
17+
val ideOrchestrator: ClientHelper,
1618
val cs: CoroutineScope,
1719
val logger: Logger,
1820
val i18n: LocalizableStringFactory,

src/main/kotlin/com/coder/toolbox/CoderToolboxExtension.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.jetbrains.toolbox.api.core.diagnostics.Logger
77
import com.jetbrains.toolbox.api.localization.LocalizableStringFactory
88
import com.jetbrains.toolbox.api.remoteDev.RemoteDevExtension
99
import com.jetbrains.toolbox.api.remoteDev.RemoteProvider
10+
import com.jetbrains.toolbox.api.remoteDev.connection.ClientHelper
1011
import com.jetbrains.toolbox.api.remoteDev.states.EnvironmentStateColorPalette
1112
import com.jetbrains.toolbox.api.remoteDev.ui.EnvironmentUiPageManager
1213
import com.jetbrains.toolbox.api.ui.ToolboxUi
@@ -24,6 +25,7 @@ class CoderToolboxExtension : RemoteDevExtension {
2425
serviceLocator.getService(ToolboxUi::class.java),
2526
serviceLocator.getService(EnvironmentUiPageManager::class.java),
2627
serviceLocator.getService(EnvironmentStateColorPalette::class.java),
28+
serviceLocator.getService(ClientHelper::class.java),
2729
serviceLocator.getService(CoroutineScope::class.java),
2830
serviceLocator.getService(Logger::class.java),
2931
serviceLocator.getService(LocalizableStringFactory::class.java),

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ fun ensureCLI(
6060
deploymentURL: URL,
6161
buildVersion: String,
6262
settings: CoderSettings,
63-
indicator: ((t: String) -> Unit)? = null,
6463
): CoderCLIManager {
6564
val cli = CoderCLIManager(deploymentURL, context.logger, settings)
6665

@@ -76,7 +75,7 @@ fun ensureCLI(
7675

7776
// If downloads are enabled download the new version.
7877
if (settings.enableDownloads) {
79-
indicator?.invoke("Downloading Coder CLI...")
78+
context.logger.info("Downloading Coder CLI...")
8079
try {
8180
cli.download()
8281
return cli
@@ -98,7 +97,7 @@ fun ensureCLI(
9897
}
9998

10099
if (settings.enableDownloads) {
101-
indicator?.invoke("Downloading Coder CLI...")
100+
context.logger.info("Downloading Coder CLI...")
102101
dataCLI.download()
103102
return dataCLI
104103
}

src/main/kotlin/com/coder/toolbox/sdk/CoderRestClient.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,19 @@ open class CoderRestClient(
169169
return workspacesResponse.body()!!.workspaces
170170
}
171171

172+
/**
173+
* Retrieves a workspace with the provided id.
174+
* @throws [APIResponseException].
175+
*/
176+
fun workspace(workspaceID: UUID): Workspace {
177+
val workspacesResponse = retroRestClient.workspace(workspaceID).execute()
178+
if (!workspacesResponse.isSuccessful) {
179+
throw APIResponseException("retrieve workspace", url, workspacesResponse)
180+
}
181+
182+
return workspacesResponse.body()!!
183+
}
184+
172185
/**
173186
* Retrieves all the agent names for all workspaces, including those that
174187
* are off. Meant to be used when configuring SSH.
@@ -272,4 +285,12 @@ open class CoderRestClient(
272285
}
273286
return buildResponse.body()!!
274287
}
288+
289+
fun close() {
290+
httpClient.apply {
291+
dispatcher.executorService.shutdown()
292+
connectionPool.evictAll()
293+
cache?.close()
294+
}
295+
}
275296
}

src/main/kotlin/com/coder/toolbox/sdk/v2/CoderV2RestFacade.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.coder.toolbox.sdk.v2.models.BuildInfo
44
import com.coder.toolbox.sdk.v2.models.CreateWorkspaceBuildRequest
55
import com.coder.toolbox.sdk.v2.models.Template
66
import com.coder.toolbox.sdk.v2.models.User
7+
import com.coder.toolbox.sdk.v2.models.Workspace
78
import com.coder.toolbox.sdk.v2.models.WorkspaceBuild
89
import com.coder.toolbox.sdk.v2.models.WorkspaceResource
910
import com.coder.toolbox.sdk.v2.models.WorkspacesResponse
@@ -30,6 +31,14 @@ interface CoderV2RestFacade {
3031
@Query("q") searchParams: String,
3132
): Call<WorkspacesResponse>
3233

34+
/**
35+
* Retrieves a workspace with the provided id.
36+
*/
37+
@GET("api/v2/workspaces/{workspaceID}")
38+
fun workspace(
39+
@Path("workspaceID") workspaceID: UUID
40+
): Call<Workspace>
41+
3342
@GET("api/v2/buildinfo")
3443
fun buildInfo(): Call<BuildInfo>
3544

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy