Skip to content

Commit e4d9fbf

Browse files
committed
fix: pass the folder parameter to the IDE&Project dialog
The value of the `folder` URI parameter is passed to the IDE&Project dialog when handling URIs. The folder value will be rendered instead of the default home folder if the value is not blank. -resolves #466
1 parent 92234b3 commit e4d9fbf

File tree

5 files changed

+75
-32
lines changed

5 files changed

+75
-32
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
### Fixed
1212

1313
- installed EAP, RC, NIGHTLY and PREVIEW IDEs are no longer displayed if there is a higher released version available for download.
14+
- project path is prefilled with the `folder` URI parameter when the IDE&Project dialog opens for URI handling.
1415

1516
## 2.19.0 - 2025-02-21
1617

src/main/kotlin/com/coder/gateway/util/Dialogs.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ private class CoderWorkspaceStepDialog(
3838

3939
init {
4040
init()
41-
title = CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.choose.text", CoderCLIManager.getWorkspaceParts(state.workspace, state.agent))
41+
title = CoderGatewayBundle.message(
42+
"gateway.connector.view.coder.remoteproject.choose.text",
43+
CoderCLIManager.getWorkspaceParts(state.workspace, state.agent)
44+
)
4245
}
4346

4447
override fun show() {
@@ -75,12 +78,13 @@ fun askIDE(
7578
cli: CoderCLIManager,
7679
client: CoderRestClient,
7780
workspaces: List<Workspace>,
81+
remoteProjectPath: String? = null
7882
): WorkspaceProjectIDE? {
7983
var data: WorkspaceProjectIDE? = null
8084
ApplicationManager.getApplication().invokeAndWait {
8185
val dialog =
8286
CoderWorkspaceStepDialog(
83-
CoderWorkspacesStepSelection(agent, workspace, cli, client, workspaces),
87+
CoderWorkspacesStepSelection(agent, workspace, cli, client, workspaces, remoteProjectPath),
8488
)
8589
data = dialog.showAndGetData()
8690
}

src/main/kotlin/com/coder/gateway/util/LinkHandler.kt

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ open class LinkHandler(
3232
parameters: Map<String, String>,
3333
indicator: ((t: String) -> Unit)? = null,
3434
): WorkspaceProjectIDE {
35-
val deploymentURL = parameters.url() ?: dialogUi.ask("Deployment URL", "Enter the full URL of your Coder deployment")
35+
val deploymentURL =
36+
parameters.url() ?: dialogUi.ask("Deployment URL", "Enter the full URL of your Coder deployment")
3637
if (deploymentURL.isNullOrBlank()) {
3738
throw MissingArgumentException("Query parameter \"$URL\" is missing")
3839
}
@@ -50,7 +51,8 @@ open class LinkHandler(
5051
}
5152

5253
// TODO: Show a dropdown and ask for the workspace if missing.
53-
val workspaceName = parameters.workspace() ?: throw MissingArgumentException("Query parameter \"$WORKSPACE\" is missing")
54+
val workspaceName =
55+
parameters.workspace() ?: throw MissingArgumentException("Query parameter \"$WORKSPACE\" is missing")
5456

5557
// The owner was added to support getting into another user's workspace
5658
// but may not exist if the Coder Gateway module is out of date. If no
@@ -65,9 +67,9 @@ open class LinkHandler(
6567
indicator,
6668
)
6769

68-
var workspace : Workspace
69-
var workspaces : List<Workspace> = emptyList()
70-
var workspacesAndAgents : Set<Pair<Workspace, WorkspaceAgent>> = emptySet()
70+
var workspace: Workspace
71+
var workspaces: List<Workspace> = emptyList()
72+
var workspacesAndAgents: Set<Pair<Workspace, WorkspaceAgent>> = emptySet()
7173
if (cli.features.wildcardSSH) {
7274
workspace = client.workspaceByOwnerAndName(owner, workspaceName)
7375
} else {
@@ -83,19 +85,28 @@ open class LinkHandler(
8385
WorkspaceStatus.PENDING, WorkspaceStatus.STARTING ->
8486
// TODO: Wait for the workspace to turn on.
8587
throw IllegalArgumentException(
86-
"The workspace \"$workspaceName\" is ${workspace.latestBuild.status.toString().lowercase()}; please wait then try again",
88+
"The workspace \"$workspaceName\" is ${
89+
workspace.latestBuild.status.toString().lowercase()
90+
}; please wait then try again",
8791
)
92+
8893
WorkspaceStatus.STOPPING, WorkspaceStatus.STOPPED,
8994
WorkspaceStatus.CANCELING, WorkspaceStatus.CANCELED,
90-
->
95+
->
9196
// TODO: Turn on the workspace.
9297
throw IllegalArgumentException(
93-
"The workspace \"$workspaceName\" is ${workspace.latestBuild.status.toString().lowercase()}; please start the workspace and try again",
98+
"The workspace \"$workspaceName\" is ${
99+
workspace.latestBuild.status.toString().lowercase()
100+
}; please start the workspace and try again",
94101
)
102+
95103
WorkspaceStatus.FAILED, WorkspaceStatus.DELETING, WorkspaceStatus.DELETED ->
96104
throw IllegalArgumentException(
97-
"The workspace \"$workspaceName\" is ${workspace.latestBuild.status.toString().lowercase()}; unable to connect",
105+
"The workspace \"$workspaceName\" is ${
106+
workspace.latestBuild.status.toString().lowercase()
107+
}; unable to connect",
98108
)
109+
99110
WorkspaceStatus.RUNNING -> Unit // All is well
100111
}
101112

@@ -106,10 +117,16 @@ open class LinkHandler(
106117
if (status.pending()) {
107118
// TODO: Wait for the agent to be ready.
108119
throw IllegalArgumentException(
109-
"The agent \"${agent.name}\" has a status of \"${status.toString().lowercase()}\"; please wait then try again",
120+
"The agent \"${agent.name}\" has a status of \"${
121+
status.toString().lowercase()
122+
}\"; please wait then try again",
110123
)
111124
} else if (!status.ready()) {
112-
throw IllegalArgumentException("The agent \"${agent.name}\" has a status of \"${status.toString().lowercase()}\"; unable to connect")
125+
throw IllegalArgumentException(
126+
"The agent \"${agent.name}\" has a status of \"${
127+
status.toString().lowercase()
128+
}\"; unable to connect"
129+
)
113130
}
114131

115132
// We only need to log in if we are using token-based auth.
@@ -123,12 +140,13 @@ open class LinkHandler(
123140

124141
val openDialog =
125142
parameters.ideProductCode().isNullOrBlank() ||
126-
parameters.ideBuildNumber().isNullOrBlank() ||
127-
(parameters.idePathOnHost().isNullOrBlank() && parameters.ideDownloadLink().isNullOrBlank()) ||
128-
parameters.folder().isNullOrBlank()
143+
parameters.ideBuildNumber().isNullOrBlank() ||
144+
(parameters.idePathOnHost().isNullOrBlank() && parameters.ideDownloadLink().isNullOrBlank()) ||
145+
parameters.folder().isNullOrBlank()
129146

130147
return if (openDialog) {
131-
askIDE(agent, workspace, cli, client, workspaces) ?: throw MissingArgumentException("IDE selection aborted; unable to connect")
148+
askIDE(agent, workspace, cli, client, workspaces, parameters.folder())
149+
?: throw MissingArgumentException("IDE selection aborted; unable to connect")
132150
} else {
133151
// Check that both the domain and the redirected domain are
134152
// allowlisted. If not, check with the user whether to proceed.
@@ -259,7 +277,7 @@ private fun isAllowlisted(url: URL): Triple<Boolean, Boolean, String> {
259277

260278
val allowlisted =
261279
domainAllowlist.any { url.host == it || url.host.endsWith(".$it") } &&
262-
domainAllowlist.any { finalUrl.host == it || finalUrl.host.endsWith(".$it") }
280+
domainAllowlist.any { finalUrl.host == it || finalUrl.host.endsWith(".$it") }
263281
val https = url.protocol == "https" && finalUrl.protocol == "https"
264282
return Triple(allowlisted, https, linkWithRedirect)
265283
}

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ private fun displayIdeWithStatus(ideWithStatus: IdeWithStatus): String =
9595
* to select an IDE and project to run on the workspace.
9696
*/
9797
class CoderWorkspaceProjectIDEStepView(
98-
private val showTitle: Boolean = true,
98+
private val showTitle: Boolean = true
9999
) : CoderWizardStep<WorkspaceProjectIDE>(
100100
CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.next.text"),
101101
) {
@@ -200,7 +200,9 @@ class CoderWorkspaceProjectIDEStepView(
200200
val name = CoderCLIManager.getWorkspaceParts(data.workspace, data.agent)
201201
logger.info("Initializing workspace step for $name")
202202

203-
val homeDirectory = data.agent.expandedDirectory ?: data.agent.directory
203+
val homeDirectory = data.remoteProjectPath.takeIf { !it.isNullOrBlank() }
204+
?: data.agent.expandedDirectory
205+
?: data.agent.directory
204206
tfProject.text = if (homeDirectory.isNullOrBlank()) "/home" else homeDirectory
205207
titleLabel.text = CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.choose.text", name)
206208
titleLabel.isVisible = showTitle
@@ -429,7 +431,7 @@ class CoderWorkspaceProjectIDEStepView(
429431
if (remainingInstalledIdes.size < installedIdes.size) {
430432
logger.info(
431433
"Skipping the following list of installed IDEs because there is already a released version " +
432-
"available for download: ${(installedIdes - remainingInstalledIdes).joinToString { "${it.product.productCode} ${it.presentableVersion}" }}"
434+
"available for download: ${(installedIdes - remainingInstalledIdes).joinToString { "${it.product.productCode} ${it.presentableVersion}" }}"
433435
)
434436
}
435437
return remainingInstalledIdes.map { it.toIdeWithStatus() }.sorted() + availableIdes.map { it.toIdeWithStatus() }

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

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ data class CoderWorkspacesStepSelection(
105105
// Pass along the latest workspaces so we can configure the CLI a bit
106106
// faster, otherwise this step would have to fetch the workspaces again.
107107
val workspaces: List<Workspace>,
108+
val remoteProjectPath: String? = null
108109
)
109110

110111
/**
@@ -147,7 +148,8 @@ class CoderWorkspacesStepView :
147148
setEmptyState(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.connect.text.disconnected"))
148149
setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
149150
selectionModel.addListSelectionListener {
150-
nextButton.isEnabled = selectedObject?.status?.ready() == true && selectedObject?.agent?.operatingSystem == OS.LINUX
151+
nextButton.isEnabled =
152+
selectedObject?.status?.ready() == true && selectedObject?.agent?.operatingSystem == OS.LINUX
151153
if (selectedObject?.status?.ready() == true && selectedObject?.agent?.operatingSystem != OS.LINUX) {
152154
notificationBanner.apply {
153155
component.isVisible = true
@@ -343,22 +345,26 @@ class CoderWorkspacesStepView :
343345
val maxWait = Duration.ofMinutes(10)
344346
while (isActive) { // Wait for the workspace to fully stop.
345347
delay(timeout.toMillis())
346-
val found = tableOfWorkspaces.items.firstOrNull { it.workspace.id == workspace.id }
348+
val found =
349+
tableOfWorkspaces.items.firstOrNull { it.workspace.id == workspace.id }
347350
when (val status = found?.workspace?.latestBuild?.status) {
348351
WorkspaceStatus.PENDING, WorkspaceStatus.STOPPING, WorkspaceStatus.RUNNING -> {
349352
logger.info("Still waiting for ${workspace.name} to stop before updating")
350353
}
354+
351355
WorkspaceStatus.STARTING, WorkspaceStatus.FAILED,
352356
WorkspaceStatus.CANCELING, WorkspaceStatus.CANCELED,
353357
WorkspaceStatus.DELETING, WorkspaceStatus.DELETED,
354-
-> {
358+
-> {
355359
logger.warn("Canceled ${workspace.name} update due to status change to $status")
356360
break
357361
}
362+
358363
null -> {
359364
logger.warn("Canceled ${workspace.name} update because it no longer exists")
360365
break
361366
}
367+
362368
WorkspaceStatus.STOPPED -> {
363369
logger.info("${workspace.name} has stopped; updating now")
364370
c.updateWorkspace(workspace)
@@ -560,7 +566,10 @@ class CoderWorkspacesStepView :
560566
deploymentURL.host,
561567
)
562568
tableOfWorkspaces.setEmptyState(
563-
CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.connect.text.connecting", deploymentURL.host),
569+
CoderGatewayBundle.message(
570+
"gateway.connector.view.coder.workspaces.connect.text.connecting",
571+
deploymentURL.host
572+
),
564573
)
565574

566575
tableOfWorkspaces.listTableModel.items = emptyList()
@@ -600,7 +609,10 @@ class CoderWorkspacesStepView :
600609
client = authedClient
601610

602611
tableOfWorkspaces.setEmptyState(
603-
CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.connect.text.connected", deploymentURL.host),
612+
CoderGatewayBundle.message(
613+
"gateway.connector.view.coder.workspaces.connect.text.connected",
614+
deploymentURL.host
615+
),
604616
)
605617
tfUrlComment?.text =
606618
CoderGatewayBundle.message(
@@ -788,7 +800,8 @@ class WorkspacesTableModel :
788800
WorkspaceVersionColumnInfo("Version"),
789801
WorkspaceStatusColumnInfo("Status"),
790802
) {
791-
private class WorkspaceIconColumnInfo(columnName: String) : ColumnInfo<WorkspaceAgentListModel, String>(columnName) {
803+
private class WorkspaceIconColumnInfo(columnName: String) :
804+
ColumnInfo<WorkspaceAgentListModel, String>(columnName) {
792805
override fun valueOf(item: WorkspaceAgentListModel?): String? = item?.workspace?.templateName
793806

794807
override fun getRenderer(item: WorkspaceAgentListModel?): TableCellRenderer {
@@ -820,7 +833,8 @@ class WorkspacesTableModel :
820833
}
821834
}
822835

823-
private class WorkspaceNameColumnInfo(columnName: String) : ColumnInfo<WorkspaceAgentListModel, String>(columnName) {
836+
private class WorkspaceNameColumnInfo(columnName: String) :
837+
ColumnInfo<WorkspaceAgentListModel, String>(columnName) {
824838
override fun valueOf(item: WorkspaceAgentListModel?): String? = item?.name
825839

826840
override fun getComparator(): Comparator<WorkspaceAgentListModel> = Comparator { a, b ->
@@ -850,7 +864,8 @@ class WorkspacesTableModel :
850864
}
851865
}
852866

853-
private class WorkspaceOwnerColumnInfo(columnName: String) : ColumnInfo<WorkspaceAgentListModel, String>(columnName) {
867+
private class WorkspaceOwnerColumnInfo(columnName: String) :
868+
ColumnInfo<WorkspaceAgentListModel, String>(columnName) {
854869
override fun valueOf(item: WorkspaceAgentListModel?): String? = item?.workspace?.ownerName
855870

856871
override fun getComparator(): Comparator<WorkspaceAgentListModel> = Comparator { a, b ->
@@ -880,7 +895,8 @@ class WorkspacesTableModel :
880895
}
881896
}
882897

883-
private class WorkspaceTemplateNameColumnInfo(columnName: String) : ColumnInfo<WorkspaceAgentListModel, String>(columnName) {
898+
private class WorkspaceTemplateNameColumnInfo(columnName: String) :
899+
ColumnInfo<WorkspaceAgentListModel, String>(columnName) {
884900
override fun valueOf(item: WorkspaceAgentListModel?): String? = item?.workspace?.templateName
885901

886902
override fun getComparator(): java.util.Comparator<WorkspaceAgentListModel> = Comparator { a, b ->
@@ -909,7 +925,8 @@ class WorkspacesTableModel :
909925
}
910926
}
911927

912-
private class WorkspaceVersionColumnInfo(columnName: String) : ColumnInfo<WorkspaceAgentListModel, String>(columnName) {
928+
private class WorkspaceVersionColumnInfo(columnName: String) :
929+
ColumnInfo<WorkspaceAgentListModel, String>(columnName) {
913930
override fun valueOf(workspace: WorkspaceAgentListModel?): String? = if (workspace == null) {
914931
"Unknown"
915932
} else if (workspace.workspace.outdated) {
@@ -940,7 +957,8 @@ class WorkspacesTableModel :
940957
}
941958
}
942959

943-
private class WorkspaceStatusColumnInfo(columnName: String) : ColumnInfo<WorkspaceAgentListModel, String>(columnName) {
960+
private class WorkspaceStatusColumnInfo(columnName: String) :
961+
ColumnInfo<WorkspaceAgentListModel, String>(columnName) {
944962
override fun valueOf(item: WorkspaceAgentListModel?): String? = item?.status?.label
945963

946964
override fun getComparator(): java.util.Comparator<WorkspaceAgentListModel> = Comparator { a, b ->

0 commit comments

Comments
 (0)