-
Notifications
You must be signed in to change notification settings - Fork 23
WIP: feat: crud workspaces #72
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
Changes from all commits
66bd03a
4612bea
255d6b6
e9813d8
4024189
d31ba11
2be39ce
ddfe7f8
035af4a
b011502
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,4 +4,5 @@ | |
/.vscode-test/ | ||
/.nyc_output/ | ||
/coverage/ | ||
*.vsix | ||
*.vsix | ||
yarn-error.log |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Workspace, WorkspaceAgent } from "coder/site/src/api/typesGenerated" | ||
|
||
export function extractAgentsAndFolderPath( | ||
workspace: Workspace, | ||
): [agents: WorkspaceAgent[], folderPath: string | undefined] { | ||
// TODO: multiple agent support | ||
const agents = workspace.latest_build.resources.reduce((acc, resource) => { | ||
return acc.concat(resource.agents || []) | ||
}, [] as WorkspaceAgent[]) | ||
|
||
let folderPath = undefined | ||
if (agents.length === 1) { | ||
folderPath = agents[0].expanded_directory | ||
} | ||
return [agents, folderPath] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
import axios from "axios" | ||
import { getAuthenticatedUser, getWorkspaces, updateWorkspaceVersion } from "coder/site/src/api/api" | ||
import { Workspace, WorkspaceAgent } from "coder/site/src/api/typesGenerated" | ||
import { Workspace } from "coder/site/src/api/typesGenerated" | ||
import * as vscode from "vscode" | ||
import { extractAgentsAndFolderPath } from "./api-helper" | ||
import { Remote } from "./remote" | ||
import { Storage } from "./storage" | ||
import { WorkspaceTreeItem } from "./workspacesProvider" | ||
|
||
export class Commands { | ||
public constructor(private readonly vscodeProposed: typeof vscode, private readonly storage: Storage) {} | ||
|
@@ -79,6 +81,9 @@ export class Commands { | |
throw new Error("Failed to get authenticated user") | ||
} | ||
await vscode.commands.executeCommand("setContext", "coder.authenticated", true) | ||
if (user.roles.find((role) => role.name === "owner")) { | ||
await vscode.commands.executeCommand("setContext", "coder.isOwner", true) | ||
} | ||
Comment on lines
+84
to
+86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to check for the owner role? We prefer to do capability checks. You can send a payload to {
"checks":{
"readAllUsers":{
"object":{
"resource_type":"user"
},
"action":"read"
},
},
} Is this just to see if we have arbitrary execution of all workspaces? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is ok for now, we but we might want to change this soon, especially with coder/coder#6875 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We decided to only show the "all Workspaces" view for users who have the owner role. sure we can revisit this after the owner connections PR |
||
vscode.window | ||
.showInformationMessage( | ||
`Welcome to Coder, ${user.username}!`, | ||
|
@@ -108,7 +113,37 @@ export class Commands { | |
}) | ||
} | ||
|
||
public async open(...args: string[]): Promise<void> { | ||
public async createWorkspace(): Promise<void> { | ||
const uri = this.storage.getURL() + "/templates" | ||
await vscode.commands.executeCommand("vscode.open", uri) | ||
} | ||
|
||
public async navigateToWorkspace(workspace: WorkspaceTreeItem) { | ||
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}` | ||
await vscode.commands.executeCommand("vscode.open", uri) | ||
} else { | ||
vscode.window.showInformationMessage("No workspace found.") | ||
} | ||
} | ||
|
||
public async navigateToWorkspaceSettings(workspace: WorkspaceTreeItem) { | ||
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` | ||
await vscode.commands.executeCommand("vscode.open", uri) | ||
} else { | ||
vscode.window.showInformationMessage("No workspace found.") | ||
} | ||
} | ||
|
||
public async open(...args: unknown[]): Promise<void> { | ||
let workspaceOwner: string | ||
let workspaceName: string | ||
let folderPath: string | undefined | ||
|
@@ -165,19 +200,19 @@ export class Commands { | |
workspaceOwner = workspace.owner_name | ||
workspaceName = workspace.name | ||
|
||
// TODO: multiple agent support | ||
const agents = workspace.latest_build.resources.reduce((acc, resource) => { | ||
return acc.concat(resource.agents || []) | ||
}, [] as WorkspaceAgent[]) | ||
|
||
if (agents.length === 1) { | ||
folderPath = agents[0].expanded_directory | ||
} | ||
const [, folderPathExtracted] = extractAgentsAndFolderPath(workspace) | ||
folderPath = folderPathExtracted | ||
} else if (args.length === 2) { | ||
// opening a workspace from the sidebar | ||
const workspaceTreeItem = args[0] as WorkspaceTreeItem | ||
workspaceOwner = workspaceTreeItem.workspaceOwner | ||
workspaceName = workspaceTreeItem.workspaceName | ||
folderPath = workspaceTreeItem.workspaceFolderPath | ||
} else { | ||
workspaceOwner = args[0] | ||
workspaceName = args[1] | ||
workspaceOwner = args[0] as string | ||
workspaceName = args[1] as string | ||
// workspaceAgent is reserved for args[2], but multiple agents aren't supported yet. | ||
folderPath = args[3] | ||
folderPath = args[3] as string | undefined | ||
} | ||
|
||
// A workspace can have multiple agents, but that's handled | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { getWorkspaces } from "coder/site/src/api/api" | ||
import * as path from "path" | ||
import * as vscode from "vscode" | ||
import { extractAgentsAndFolderPath } from "./api-helper" | ||
|
||
export enum WorkspaceQuery { | ||
Mine = "owner:me", | ||
All = "", | ||
} | ||
|
||
export class WorkspaceProvider implements vscode.TreeDataProvider<WorkspaceTreeItem> { | ||
constructor(private readonly getWorkspacesQuery: WorkspaceQuery) {} | ||
|
||
private _onDidChangeTreeData: vscode.EventEmitter<WorkspaceTreeItem | undefined | null | void> = | ||
new vscode.EventEmitter<WorkspaceTreeItem | undefined | null | void>() | ||
readonly onDidChangeTreeData: vscode.Event<WorkspaceTreeItem | undefined | null | void> = | ||
this._onDidChangeTreeData.event | ||
|
||
refresh(): void { | ||
this._onDidChangeTreeData.fire() | ||
} | ||
|
||
getTreeItem(element: WorkspaceTreeItem): vscode.TreeItem { | ||
return element | ||
} | ||
|
||
getChildren(): Thenable<WorkspaceTreeItem[]> { | ||
return getWorkspaces({ q: this.getWorkspacesQuery }).then((workspaces) => { | ||
return workspaces.workspaces.map((workspace) => { | ||
const status = | ||
workspace.latest_build.status.substring(0, 1).toUpperCase() + workspace.latest_build.status.substring(1) | ||
|
||
const label = | ||
this.getWorkspacesQuery === WorkspaceQuery.All | ||
? `${workspace.owner_name} / ${workspace.name}` | ||
: workspace.name | ||
const detail = `Template: ${workspace.template_display_name || workspace.template_name} • Status: ${status}` | ||
const [, folderPath] = extractAgentsAndFolderPath(workspace) | ||
return new WorkspaceTreeItem(label, detail, workspace.owner_name, workspace.name, folderPath) | ||
}) | ||
}) | ||
} | ||
} | ||
|
||
export class WorkspaceTreeItem extends vscode.TreeItem { | ||
constructor( | ||
public readonly label: string, | ||
public readonly tooltip: string, | ||
public readonly workspaceOwner: string, | ||
public readonly workspaceName: string, | ||
public readonly workspaceFolderPath: string | undefined, | ||
) { | ||
super(label, vscode.TreeItemCollapsibleState.None) | ||
} | ||
|
||
iconPath = { | ||
light: path.join(__filename, "..", "..", "media", "logo.svg"), | ||
dark: path.join(__filename, "..", "..", "media", "logo.svg"), | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there is more than 1 agent, does everything fail? Should we randomly choose one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a good question. I am not changing the actual behavior, just extracted it to a new function. the current version also does this
agents[0]
choice =/ . I am not sure what is the best way to handle multiple agents...