Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit5e11642

Browse files
committed
Break out link handler
This is to share as much as possible with the Toolbox branch.Part of this added a URI query param parser, since in Toolbox we get theraw URI and there does not seem to be anything in Kotlin to parse queryparameters.
1 parent8adf608 commit5e11642

File tree

11 files changed

+699
-646
lines changed

11 files changed

+699
-646
lines changed

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

Lines changed: 5 additions & 323 deletions
Original file line numberDiff line numberDiff line change
@@ -2,94 +2,14 @@
22

33
packagecom.coder.gateway
44

5-
importcom.coder.gateway.cli.CoderCLIManager
6-
importcom.coder.gateway.cli.ensureCLI
7-
importcom.coder.gateway.models.AGENT_ID
8-
importcom.coder.gateway.models.AGENT_NAME
9-
importcom.coder.gateway.models.TOKEN
10-
importcom.coder.gateway.models.URL
11-
importcom.coder.gateway.models.WORKSPACE
12-
importcom.coder.gateway.models.WorkspaceAndAgentStatus
13-
importcom.coder.gateway.models.WorkspaceProjectIDE
14-
importcom.coder.gateway.models.agentID
15-
importcom.coder.gateway.models.agentName
16-
importcom.coder.gateway.models.folder
17-
importcom.coder.gateway.models.ideBuildNumber
18-
importcom.coder.gateway.models.ideDownloadLink
19-
importcom.coder.gateway.models.idePathOnHost
20-
importcom.coder.gateway.models.ideProductCode
21-
importcom.coder.gateway.models.isCoder
22-
importcom.coder.gateway.models.token
23-
importcom.coder.gateway.models.url
24-
importcom.coder.gateway.models.workspace
25-
importcom.coder.gateway.sdk.CoderRestClient
26-
importcom.coder.gateway.sdk.ex.APIResponseException
27-
importcom.coder.gateway.sdk.v2.models.Workspace
28-
importcom.coder.gateway.sdk.v2.models.WorkspaceAgent
29-
importcom.coder.gateway.sdk.v2.models.WorkspaceStatus
30-
importcom.coder.gateway.services.CoderRestClientService
315
importcom.coder.gateway.services.CoderSettingsService
32-
importcom.coder.gateway.settings.Source
33-
importcom.coder.gateway.util.toURL
34-
importcom.coder.gateway.views.steps.CoderWorkspaceProjectIDEStepView
35-
importcom.coder.gateway.views.steps.CoderWorkspacesStepSelection
36-
importcom.intellij.openapi.application.ApplicationManager
6+
importcom.coder.gateway.util.handleLink
7+
importcom.coder.gateway.util.isCoder
378
importcom.intellij.openapi.components.service
389
importcom.intellij.openapi.diagnostic.Logger
39-
importcom.intellij.openapi.ui.DialogWrapper
40-
importcom.intellij.ui.dsl.builder.panel
41-
importcom.intellij.util.ui.JBUI
4210
importcom.jetbrains.gateway.api.ConnectionRequestor
4311
importcom.jetbrains.gateway.api.GatewayConnectionHandle
4412
importcom.jetbrains.gateway.api.GatewayConnectionProvider
45-
importjavax.swing.JComponent
46-
importjavax.swing.border.Border
47-
48-
/**
49-
* A dialog wrapper around CoderWorkspaceStepView.
50-
*/
51-
classCoderWorkspaceStepDialog(
52-
name:String,
53-
privatevalstate:CoderWorkspacesStepSelection,
54-
) : DialogWrapper(true) {
55-
privateval view=CoderWorkspaceProjectIDEStepView(showTitle=false)
56-
57-
init {
58-
init()
59-
title=CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.choose.text", name)
60-
}
61-
62-
overridefunshow() {
63-
view.init(state)
64-
view.onPrevious= { close(1) }
65-
view.onNext= { close(0) }
66-
super.show()
67-
view.dispose()
68-
}
69-
70-
funshowAndGetData():WorkspaceProjectIDE? {
71-
if (showAndGet()) {
72-
return view.data()
73-
}
74-
returnnull
75-
}
76-
77-
overridefuncreateContentPaneBorder():Border {
78-
returnJBUI.Borders.empty()
79-
}
80-
81-
overridefuncreateCenterPanel():JComponent {
82-
return view
83-
}
84-
85-
overridefuncreateSouthPanel():JComponent {
86-
// The plugin provides its own buttons.
87-
// TODO: Is it more idiomatic to handle buttons out here?
88-
return panel {}.apply {
89-
border=JBUI.Borders.empty()
90-
}
91-
}
92-
}
9313

9414
// CoderGatewayConnectionProvider handles connecting via a Gateway link such as
9515
// jetbrains-gateway://connect#type=coder.
@@ -101,204 +21,14 @@ class CoderGatewayConnectionProvider : GatewayConnectionProvider {
10121
requestor:ConnectionRequestor,
10222
):GatewayConnectionHandle? {
10323
CoderRemoteConnectionHandle().connect { indicator->
104-
logger.debug("Launched Coder connection provider", parameters)
105-
106-
val deploymentURL=
107-
parameters.url()
108-
?:CoderRemoteConnectionHandle.ask("Enter the full URL of your Coder deployment")
109-
if (deploymentURL.isNullOrBlank()) {
110-
throwIllegalArgumentException("Query parameter\"$URL\" is missing")
111-
}
112-
113-
val client= authenticate(deploymentURL, parameters.token())
114-
115-
// TODO: If the workspace is missing we could launch the wizard.
116-
val workspaceName= parameters.workspace()?:throwIllegalArgumentException("Query parameter\"$WORKSPACE\" is missing")
117-
118-
val workspaces= client.workspaces()
119-
val workspace=
120-
workspaces.firstOrNull {
121-
it.name== workspaceName
122-
}?:throwIllegalArgumentException("The workspace$workspaceName does not exist")
123-
124-
when (workspace.latestBuild.status) {
125-
WorkspaceStatus.PENDING,WorkspaceStatus.STARTING->
126-
// TODO: Wait for the workspace to turn on.
127-
throwIllegalArgumentException(
128-
"The workspace\"$workspaceName\" is${workspace.latestBuild.status.toString().lowercase()}; please wait then try again",
129-
)
130-
WorkspaceStatus.STOPPING,WorkspaceStatus.STOPPED,
131-
WorkspaceStatus.CANCELING,WorkspaceStatus.CANCELED,
132-
->
133-
// TODO: Turn on the workspace.
134-
throwIllegalArgumentException(
135-
"The workspace\"$workspaceName\" is${workspace.latestBuild.status.toString().lowercase()}; please start the workspace and try again",
136-
)
137-
WorkspaceStatus.FAILED,WorkspaceStatus.DELETING,WorkspaceStatus.DELETED->
138-
throwIllegalArgumentException(
139-
"The workspace\"$workspaceName\" is${workspace.latestBuild.status.toString().lowercase()}; unable to connect",
140-
)
141-
WorkspaceStatus.RUNNING->Unit// All is well
142-
}
143-
144-
// TODO: Show a dropdown and ask for an agent if missing.
145-
val agent= getMatchingAgent(parameters, workspace)
146-
val status=WorkspaceAndAgentStatus.from(workspace, agent)
147-
148-
if (status.pending()) {
149-
// TODO: Wait for the agent to be ready.
150-
throwIllegalArgumentException(
151-
"The agent\"${agent.name}\" is${status.toString().lowercase()}; please wait then try again",
152-
)
153-
}elseif (!status.ready()) {
154-
throwIllegalArgumentException("The agent\"${agent.name}\" is${status.toString().lowercase()}; unable to connect")
155-
}
156-
157-
val cli=
158-
ensureCLI(
159-
deploymentURL.toURL(),
160-
client.buildInfo().version,
161-
settings,
162-
indicator,
163-
)
164-
165-
// We only need to log in if we are using token-based auth.
166-
if (client.token!==null) {
167-
indicator.text="Authenticating Coder CLI..."
168-
cli.login(client.token)
169-
}
170-
171-
indicator.text="Configuring Coder CLI..."
172-
cli.configSsh(client.agentNames(workspaces))
173-
174-
val name="${workspace.name}.${agent.name}"
175-
val openDialog=
176-
parameters.ideProductCode().isNullOrBlank()||
177-
parameters.ideBuildNumber().isNullOrBlank()||
178-
(parameters.idePathOnHost().isNullOrBlank()&& parameters.ideDownloadLink().isNullOrBlank())||
179-
parameters.folder().isNullOrBlank()
180-
181-
if (openDialog) {
182-
var data:WorkspaceProjectIDE?=null
183-
ApplicationManager.getApplication().invokeAndWait {
184-
val dialog=
185-
CoderWorkspaceStepDialog(
186-
name,
187-
CoderWorkspacesStepSelection(agent, workspace, cli, client, workspaces),
188-
)
189-
data= dialog.showAndGetData()
190-
}
191-
data?:throwException("IDE selection aborted; unable to connect")
192-
}else {
193-
// Check that both the domain and the redirected domain are
194-
// allowlisted. If not, check with the user whether to proceed.
195-
verifyDownloadLink(parameters)
196-
WorkspaceProjectIDE.fromInputs(
197-
name= name,
198-
hostname=CoderCLIManager.getHostName(deploymentURL.toURL(), name),
199-
projectPath= parameters.folder(),
200-
ideProductCode= parameters.ideProductCode(),
201-
ideBuildNumber= parameters.ideBuildNumber(),
202-
idePathOnHost= parameters.idePathOnHost(),
203-
downloadSource= parameters.ideDownloadLink(),
204-
deploymentURL= deploymentURL,
205-
lastOpened=null,// Have not opened yet.
206-
)
24+
logger.debug("Launched Coder link handler", parameters)
25+
handleLink(parameters, settings) {
26+
indicator.text= it
20727
}
20828
}
20929
returnnull
21030
}
21131

212-
/**
213-
* Return an authenticated Coder CLI, asking for the token as long as it
214-
* continues to result in an authentication failure and token authentication
215-
* is required.
216-
*/
217-
privatefunauthenticate(
218-
deploymentURL:String,
219-
queryToken:String?,
220-
lastToken:Pair<String,Source>? = null,
221-
):CoderRestClient {
222-
val token=
223-
if (settings.requireTokenAuth) {
224-
// Use the token from the query, unless we already tried that.
225-
val isRetry= lastToken!=null
226-
if (!queryToken.isNullOrBlank()&&!isRetry) {
227-
Pair(queryToken,Source.QUERY)
228-
}else {
229-
CoderRemoteConnectionHandle.askToken(
230-
deploymentURL.toURL(),
231-
lastToken,
232-
isRetry,
233-
useExisting=true,
234-
settings,
235-
)
236-
}
237-
}else {
238-
null
239-
}
240-
if (settings.requireTokenAuth&& token==null) {// User aborted.
241-
throwIllegalArgumentException("Unable to connect to$deploymentURL, query parameter\"$TOKEN\" is missing")
242-
}
243-
val client=CoderRestClientService(deploymentURL.toURL(), token?.first)
244-
returntry {
245-
client.authenticate()
246-
client
247-
}catch (ex:APIResponseException) {
248-
// If doing token auth we can ask and try again.
249-
if (settings.requireTokenAuth&& ex.isUnauthorized) {
250-
authenticate(deploymentURL, queryToken, token)
251-
}else {
252-
throw ex
253-
}
254-
}
255-
}
256-
257-
/**
258-
* Check that the link is allowlisted. If not, confirm with the user.
259-
*/
260-
privatefunverifyDownloadLink(parameters:Map<String,String>) {
261-
val link= parameters.ideDownloadLink()
262-
if (link.isNullOrBlank()) {
263-
return// Nothing to verify
264-
}
265-
266-
val url=
267-
try {
268-
link.toURL()
269-
}catch (ex:Exception) {
270-
throwIllegalArgumentException("$link is not a valid URL")
271-
}
272-
273-
val (allowlisted, https, linkWithRedirect)=
274-
try {
275-
CoderRemoteConnectionHandle.isAllowlisted(url)
276-
}catch (e:Exception) {
277-
throwIllegalArgumentException("Unable to verify$url:$e")
278-
}
279-
if (allowlisted&& https) {
280-
return
281-
}
282-
283-
val comment=
284-
if (allowlisted) {
285-
"The download link is from a non-allowlisted URL"
286-
}elseif (https) {
287-
"The download link is not using HTTPS"
288-
}else {
289-
"The download link is from a non-allowlisted URL and is not using HTTPS"
290-
}
291-
292-
if (!CoderRemoteConnectionHandle.confirm(
293-
"Confirm download URL",
294-
"$comment. Would you like to proceed?",
295-
linkWithRedirect,
296-
)
297-
) {
298-
throwIllegalArgumentException("$linkWithRedirect is not allowlisted")
299-
}
300-
}
301-
30232
overridefunisApplicable(parameters:Map<String,String>):Boolean {
30333
return parameters.isCoder()
30434
}
@@ -307,51 +37,3 @@ class CoderGatewayConnectionProvider : GatewayConnectionProvider {
30737
val logger=Logger.getInstance(CoderGatewayConnectionProvider::class.java.simpleName)
30838
}
30939
}
310-
311-
/**
312-
* Return the agent matching the provided agent ID or name in the parameters.
313-
* The name is ignored if the ID is set. If neither was supplied and the
314-
* workspace has only one agent, return that. Otherwise throw an error.
315-
*
316-
* @throws [MissingArgumentException, IllegalArgumentException]
317-
*/
318-
fungetMatchingAgent(
319-
parameters:Map<String,String?>,
320-
workspace:Workspace,
321-
):WorkspaceAgent {
322-
val agents= workspace.latestBuild.resources.filter { it.agents!=null }.flatMap { it.agents!! }
323-
if (agents.isEmpty()) {
324-
throwIllegalArgumentException("The workspace\"${workspace.name}\" has no agents")
325-
}
326-
327-
// If the agent is missing and the workspace has only one, use that.
328-
// Prefer the ID over the name if both are set.
329-
val agent=
330-
if (!parameters.agentID().isNullOrBlank()) {
331-
agents.firstOrNull { it.id.toString()== parameters.agentID() }
332-
}elseif (!parameters.agentName().isNullOrBlank()) {
333-
agents.firstOrNull { it.name== parameters.agentName() }
334-
}elseif (agents.size==1) {
335-
agents.first()
336-
}else {
337-
null
338-
}
339-
340-
if (agent==null) {
341-
if (!parameters.agentID().isNullOrBlank()) {
342-
throwIllegalArgumentException("The workspace\"${workspace.name}\" does not have an agent with ID\"${parameters.agentID()}\"")
343-
}elseif (!parameters.agentName().isNullOrBlank()) {
344-
throwIllegalArgumentException(
345-
"The workspace\"${workspace.name}\"does not have an agent named\"${parameters.agentName()}\"",
346-
)
347-
}else {
348-
throwMissingArgumentException(
349-
"Unable to determine which agent to connect to; one of\"$AGENT_NAME\" or\"$AGENT_ID\" must be set because the workspace\"${workspace.name}\" has more than one agent",
350-
)
351-
}
352-
}
353-
354-
return agent
355-
}
356-
357-
classMissingArgumentException(message:String) : IllegalArgumentException(message)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp