-
Notifications
You must be signed in to change notification settings - Fork 180
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add API based filtering for organizations (#1472)
* Build abstract api filtering quick pick * Use new APIQuickPick for project filter * Move TerraformCloudHost We need this to open the browser for organziation creation * Add search query parameter to organizations API * Improve APIQuickPick hide and show behavior * Implement OrganizationAPIResource * Use OrganizationAPIResource to pick an organization * Move uiHelpers file
- Loading branch information
Showing
10 changed files
with
238 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/** | ||
* Copyright (c) HashiCorp, Inc. | ||
* SPDX-License-Identifier: MPL-2.0 | ||
*/ | ||
|
||
import * as vscode from 'vscode'; | ||
import { TerraformCloudHost, apiClient } from '../../terraformCloud'; | ||
import { APIResource } from './uiHelpers'; | ||
import { Organization } from '../../terraformCloud/organization'; | ||
|
||
export class CreateOrganizationItem implements vscode.QuickPickItem { | ||
get label() { | ||
return '$(add) Create new organization'; | ||
} | ||
get description() { | ||
return 'Open the browser to create a new organization'; | ||
} | ||
async open() { | ||
await vscode.env.openExternal(vscode.Uri.parse(`https://${TerraformCloudHost}/app/organizations`)); | ||
} | ||
get alwaysShow() { | ||
return true; | ||
} | ||
} | ||
|
||
export class RefreshOrganizationItem implements vscode.QuickPickItem { | ||
get label() { | ||
return '$(refresh) Refresh organizations'; | ||
} | ||
get description() { | ||
return 'Refetch all organizations'; | ||
} | ||
get alwaysShow() { | ||
return true; | ||
} | ||
} | ||
|
||
class OrganizationItem implements vscode.QuickPickItem { | ||
constructor(protected organization: Organization) {} | ||
get label() { | ||
return this.organization.attributes.name; | ||
} | ||
} | ||
|
||
export class OrganizationAPIResource implements APIResource { | ||
name = 'organizations'; | ||
title = 'Welcome to Terraform Cloud'; | ||
placeholder = 'Choose an organization. Hit enter to select the first organization. (type to search)'; | ||
ignoreFocusOut = true; | ||
|
||
private async createOrganizationItems(search?: string): Promise<OrganizationItem[]> { | ||
const organizations = await apiClient.listOrganizations({ | ||
// Include query parameter only if search argument is passed | ||
...(search && { | ||
queries: { | ||
q: search, | ||
}, | ||
}), | ||
}); | ||
|
||
return organizations.data.map((organization) => new OrganizationItem(organization)); | ||
} | ||
|
||
async fetchItems(query?: string): Promise<vscode.QuickPickItem[]> { | ||
const createItem = new CreateOrganizationItem(); | ||
const refreshItem = new RefreshOrganizationItem(); | ||
const picks: vscode.QuickPickItem[] = [ | ||
createItem, | ||
refreshItem, | ||
{ label: '', kind: vscode.QuickPickItemKind.Separator }, | ||
]; | ||
|
||
try { | ||
picks.push(...(await this.createOrganizationItems(query))); | ||
} catch (error) { | ||
let message = 'Failed to fetch organizations'; | ||
if (error instanceof Error) { | ||
message = error.message; | ||
} else if (typeof error === 'string') { | ||
message = error; | ||
} | ||
|
||
picks.push({ label: `$(error) Error: ${message}`, alwaysShow: true }); | ||
console.error(error); | ||
} | ||
|
||
return picks; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/** | ||
* Copyright (c) HashiCorp, Inc. | ||
* SPDX-License-Identifier: MPL-2.0 | ||
*/ | ||
|
||
import * as vscode from 'vscode'; | ||
|
||
export interface APIResource { | ||
readonly name: string; | ||
|
||
readonly title: string; | ||
readonly placeholder: string; | ||
readonly ignoreFocusOut?: boolean; | ||
|
||
fetchItems(query?: string): Promise<vscode.QuickPickItem[]>; | ||
} | ||
|
||
export class APIQuickPick { | ||
private quickPick: vscode.QuickPick<vscode.QuickPickItem>; | ||
private fetchTimerKey: NodeJS.Timeout | undefined; | ||
|
||
constructor(private resource: APIResource) { | ||
this.quickPick = vscode.window.createQuickPick(); | ||
this.quickPick.title = resource.title; | ||
this.quickPick.placeholder = resource.placeholder; | ||
this.quickPick.onDidChangeValue(this.onDidChangeValue, this); | ||
this.quickPick.ignoreFocusOut = resource.ignoreFocusOut ?? false; | ||
} | ||
|
||
private onDidChangeValue() { | ||
clearTimeout(this.fetchTimerKey); | ||
// Only starts fetching after a user stopped typing for 300ms | ||
this.fetchTimerKey = setTimeout(() => this.fetchResource.apply(this), 300); | ||
} | ||
|
||
private async fetchResource() { | ||
this.quickPick.busy = true; | ||
this.quickPick.show(); | ||
|
||
this.quickPick.items = await this.resource.fetchItems(this.quickPick.value); | ||
|
||
this.quickPick.busy = false; | ||
} | ||
|
||
async pick(autoHide = true) { | ||
await this.fetchResource(); | ||
|
||
const result = await new Promise<vscode.QuickPickItem | undefined>((c) => { | ||
this.quickPick.onDidAccept(() => c(this.quickPick.selectedItems[0])); | ||
this.quickPick.onDidHide(() => c(undefined)); | ||
this.quickPick.show(); | ||
}); | ||
|
||
if (autoHide) { | ||
this.quickPick.hide(); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
hide() { | ||
this.quickPick.hide(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.