Skip to content

Refactor REST client #286

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Use new client in commands for the current workspace
These should work now once the clients are pointing to different things.
  • Loading branch information
code-asher committed May 30, 2024
commit f6bafefdd4b5c836736afcddb7a39c29fcf3aa13
54 changes: 36 additions & 18 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ import { Storage } from "./storage"
import { OpenableTreeItem } from "./workspacesProvider"

export class Commands {
// These will only be populated when actively connected to a workspace and are
// used in commands. Because commands can be executed by the user, it is not
// possible to pass in arguments, so we have to store the current workspace
// and its client somewhere, separately from the current globally logged-in
// client, since you can connect to workspaces not belonging to whatever you
// are logged into (for convenience; otherwise the recents menu can be a pain
// if you use multiple deployments).
public workspace?: Workspace
public workspaceLogPath?: string
public workspaceRestClient?: Api

public constructor(
private readonly vscodeProposed: typeof vscode,
private readonly restClient: Api,
Expand Down Expand Up @@ -162,15 +173,14 @@ export class Commands {
}

/**
* View the logs for the currently logged-in deployment.
* View the logs for the currently connected workspace.
*/
public async viewLogs(): Promise<void> {
// TODO: This will need to be refactored for multi-deployment support.
if (!this.storage.workspaceLogPath) {
vscode.window.showInformationMessage("No logs available.", this.storage.workspaceLogPath || "<unset>")
if (!this.workspaceLogPath) {
vscode.window.showInformationMessage("No logs available.", this.workspaceLogPath || "<unset>")
return
}
const uri = vscode.Uri.file(this.storage.workspaceLogPath)
const uri = vscode.Uri.file(this.workspaceLogPath)
const doc = await vscode.workspace.openTextDocument(uri)
await vscode.window.showTextDocument(doc)
}
Expand Down Expand Up @@ -212,14 +222,18 @@ export class Commands {
/**
* Open a link to the workspace in the Coder dashboard.
*
* Must only be called if currently logged in.
* If passing in a workspace, it must belong to the currently logged-in
* deployment.
*
* Otherwise, the currently connected workspace is used (if any).
*/
public async navigateToWorkspace(workspace: OpenableTreeItem) {
if (workspace) {
const uri = this.storage.getUrl() + `/@${workspace.workspaceOwner}/${workspace.workspaceName}`
await vscode.commands.executeCommand("vscode.open", uri)
} else if (this.storage.workspace) {
const uri = this.storage.getUrl() + `/@${this.storage.workspace.owner_name}/${this.storage.workspace.name}`
} else if (this.workspace && this.workspaceRestClient) {
const baseUrl = this.workspaceRestClient.getAxiosInstance().defaults.baseURL
const uri = `${baseUrl}/@${this.workspace.owner_name}/${this.workspace.name}`
await vscode.commands.executeCommand("vscode.open", uri)
} else {
vscode.window.showInformationMessage("No workspace found.")
Expand All @@ -229,15 +243,18 @@ export class Commands {
/**
* Open a link to the workspace settings in the Coder dashboard.
*
* Must only be called if currently logged in.
* If passing in a workspace, it must belong to the currently logged-in
* deployment.
*
* Otherwise, the currently connected workspace is used (if any).
*/
public async navigateToWorkspaceSettings(workspace: OpenableTreeItem) {
if (workspace) {
const uri = this.storage.getUrl() + `/@${workspace.workspaceOwner}/${workspace.workspaceName}/settings`
await vscode.commands.executeCommand("vscode.open", uri)
} else if (this.storage.workspace) {
const uri =
this.storage.getUrl() + `/@${this.storage.workspace.owner_name}/${this.storage.workspace.name}/settings`
} else if (this.workspace && this.workspaceRestClient) {
const baseUrl = this.workspaceRestClient.getAxiosInstance().defaults.baseURL
const uri = `${baseUrl}/@${this.workspace.owner_name}/${this.workspace.name}/settings`
await vscode.commands.executeCommand("vscode.open", uri)
} else {
vscode.window.showInformationMessage("No workspace found.")
Expand All @@ -248,7 +265,8 @@ export class Commands {
* Open a workspace or agent that is showing in the sidebar.
*
* This essentially just builds the host name and passes it to the VS Code
* Remote SSH extension.
* Remote SSH extension, so it is not necessary to be logged in, although then
* the sidebar would not have any workspaces in it anyway.
*/
public async openFromSidebar(treeItem: OpenableTreeItem) {
if (treeItem) {
Expand All @@ -263,9 +281,9 @@ export class Commands {
}

/**
* Open a workspace from the currently logged-in deployment.
* Open a workspace belonging to the currently logged-in deployment.
*
* This must only be called if the REST client is logged in.
* This must only be called if logged into a deployment.
*/
public async open(...args: unknown[]): Promise<void> {
let workspaceOwner: string
Expand Down Expand Up @@ -393,20 +411,20 @@ export class Commands {
* this is a no-op.
*/
public async updateWorkspace(): Promise<void> {
if (!this.storage.workspace || !this.storage.restClient) {
if (!this.workspace || !this.workspaceRestClient) {
return
}
const action = await this.vscodeProposed.window.showInformationMessage(
"Update Workspace",
{
useCustom: true,
modal: true,
detail: `${this.storage.workspace.owner_name}/${this.storage.workspace.name} will be updated then this window will reload to watch the build logs and reconnect.`,
detail: `${this.workspace.owner_name}/${this.workspace.name} will be updated then this window will reload to watch the build logs and reconnect.`,
},
"Update",
)
if (action === "Update") {
await this.storage.restClient.updateWorkspaceVersion(this.storage.workspace)
await this.workspaceRestClient.updateWorkspaceVersion(this.workspace)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
if (!vscodeProposed.env.remoteAuthority) {
return
}
const remote = new Remote(vscodeProposed, storage, ctx.extensionMode)
const remote = new Remote(vscodeProposed, storage, commands, ctx.extensionMode)
try {
await remote.setup(vscodeProposed.env.remoteAuthority)
} catch (ex) {
Expand Down
14 changes: 8 additions & 6 deletions src/remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as semver from "semver"
import * as vscode from "vscode"
import * as ws from "ws"
import { makeCoderSdk } from "./api"
import { Commands } from "./commands"
import { getHeaderCommand } from "./headers"
import { SSHConfig, SSHValues, defaultSSHConfigResponse, mergeSSHConfigValues } from "./sshConfig"
import { computeSSHProperties, sshSupportsSetEnv } from "./sshSupport"
Expand All @@ -27,6 +28,7 @@ export class Remote {
public constructor(
private readonly vscodeProposed: typeof vscode,
private readonly storage: Storage,
private readonly commands: Commands,
private readonly mode: vscode.ExtensionMode,
) {}

Expand Down Expand Up @@ -82,7 +84,7 @@ export class Remote {
const token = await this.storage.getSessionToken()
const restClient = await makeCoderSdk(baseUrlRaw, token, this.storage)
// Store for use in commands.
this.storage.restClient = restClient
this.commands.workspaceRestClient = restClient

// First thing is to check the version.
const buildInfo = await restClient.getBuildInfo()
Expand Down Expand Up @@ -112,7 +114,7 @@ export class Remote {
let workspace: Workspace
try {
workspace = await restClient.getWorkspaceByOwnerAndName(parts[0], parts[1])
this.storage.workspace = workspace
this.commands.workspace = workspace
} catch (error) {
if (!isAxiosError(error)) {
throw error
Expand Down Expand Up @@ -193,7 +195,7 @@ export class Remote {
...workspace,
latest_build: latestBuild,
}
this.storage.workspace = workspace
this.commands.workspace = workspace
}

// If a build is running we should stream the logs to the user so they can
Expand Down Expand Up @@ -251,7 +253,7 @@ export class Remote {
})
writeEmitter.fire("Build complete")
workspace = await restClient.getWorkspace(workspace.id)
this.storage.workspace = workspace
this.commands.workspace = workspace
terminal.dispose()

if (buildComplete) {
Expand Down Expand Up @@ -420,7 +422,7 @@ export class Remote {
return
}
refreshWorkspaceUpdatedStatus(workspace)
this.storage.workspace = workspace
this.commands.workspace = workspace
workspaceUpdate.fire(workspace)
if (workspace.latest_build.status === "stopping" || workspace.latest_build.status === "stopped") {
const action = this.vscodeProposed.window.showInformationMessage(
Expand Down Expand Up @@ -520,7 +522,7 @@ export class Remote {
return
}
disposables.push(this.showNetworkUpdates(pid))
this.storage.workspaceLogPath = path.join(this.storage.getLogPath(), `${pid}.log`)
this.commands.workspaceLogPath = path.join(this.storage.getLogPath(), `${pid}.log`)
})

// Register the label formatter again because SSH overrides it!
Expand Down
13 changes: 0 additions & 13 deletions src/storage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Api } from "coder/site/src/api/api"
import { Workspace } from "coder/site/src/api/typesGenerated"
import { createWriteStream } from "fs"
import fs from "fs/promises"
import { ensureDir } from "fs-extra"
Expand All @@ -15,18 +14,6 @@ import { getHeaderCommand, getHeaders } from "./headers"
const MAX_URLS = 10

export class Storage {
// These will only be populated when actively connected to a workspace and are
// used in commands. Because commands can be executed by the user, it is not
// possible to pass in arguments, so we have to store the current workspace
// and client somewhere, separately from the current login, since you can
// connect to workspaces not belonging to whatever you are logged into (for
// convenience; otherwise the recents menu can be a pain if you use multiple
// deployments).
// TODO: Should maybe store on the Commands class instead.
public workspace?: Workspace
public workspaceLogPath?: string
public restClient?: Api

constructor(
private readonly output: vscode.OutputChannel,
private readonly memento: vscode.Memento,
Expand Down
2 changes: 2 additions & 0 deletions src/workspacesProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ type AgentWatcher = {
/**
* Polls workspaces using the provided REST client and renders them in a tree.
*
* Polling does not start until fetchAndRefresh() is called at least once.
*
* If the poll fails or the client has no URL configured, clear the tree and
* abort polling until fetchAndRefresh() is called again.
*/
Expand Down