Skip to content
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 to tree item picker #1232

Merged
merged 16 commits into from
Sep 7, 2022
Merged
96 changes: 79 additions & 17 deletions utils/hostapi.v2.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,54 @@
import { IActionContext } from "./index";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { AzExtTreeItem, IActionContext } from "./index";
import * as vscode from 'vscode';
import type { Environment } from '@azure/ms-rest-azure-env';
import { AzExtResourceType } from "./src/AzExtResourceType";

export interface ApplicationAuthentication {
getSession(scopes?: string[]): vscode.ProviderResult<vscode.AuthenticationSession>;
}

/**
* Information specific to the Subscription
*/
export interface ApplicationSubscription {
readonly authentication: ApplicationAuthentication;
readonly displayName: string;
readonly subscriptionId: string;
readonly environment: Environment;
readonly isCustomCloud: boolean;
}

export interface ResourceBase {
readonly id: string;
readonly name: string;
}

export interface ApplicationResourceType {
readonly type: string;
readonly kinds?: string[];
}

/**
* Represents an individual resource in Azure.
* @remarks The `id` property is expected to be the Azure resource ID.
*/
export interface ApplicationResource extends ResourceBase {
readonly subscription: ApplicationSubscription;
readonly type: ApplicationResourceType;
readonly azExtResourceType?: AzExtResourceType;
readonly location?: string;
readonly resourceGroup?: string;
/** Resource tags */
readonly tags?: {
[propertyName: string]: string;
};
/* add more properties from GenericResource if needed */
}

/**
* Interface describing an object that wraps another object.
Expand All @@ -12,28 +62,40 @@ import { IActionContext } from "./index";
* aren't boxes)
*/
export interface Box {
unwrap<T>(): Promise<T>;
unwrap<T>(): T;
}

/**
* Tests to see if something is a box, by ensuring it is an object
* and has an "unwrap" function
* @param maybeBox An object to test if it is a box
* @returns True if a box, false otherwise
*/
export declare function isBox(maybeBox: unknown): maybeBox is Box;

/**
* Describes command callbacks for tree node context menu commands
*/
export type TreeNodeCommandCallback<T> = (context: IActionContext, node?: T, nodes?: T[], ...args: any[]) => any;

/**
* Used to register VSCode tree node context menu commands that are in the host extension's tree. It wraps your callback with consistent error and telemetry handling
* Use debounce property if you need a delay between clicks for this particular command
* A telemetry event is automatically sent whenever a command is executed. The telemetry event ID will default to the same as the
* commandId passed in, but can be overridden per command with telemetryId
* The telemetry event for this command will be named telemetryId if specified, otherwise it defaults to the commandId
* NOTE: If the environment variable `DEBUGTELEMETRY` is set to a non-empty, non-zero value, then telemetry will not be sent. If the value is 'verbose' or 'v', telemetry will be displayed in the console window.
* Describes filtering based on context value. Items that pass the filter will
* match at least one of the `include` filters, but none of the `exclude` filters.
*/
export declare function registerCommandWithTreeNodeUnboxing<T>(commandId: string, callback: TreeNodeCommandCallback<T>, debounce?: number, telemetryId?: string): void;
export interface ContextValueFilter {
/**
* This filter will include items that match *any* of the values in the array.
* When a string is used, exact value comparison is done.
*/
include: string | RegExp | (string | RegExp)[];

/**
* This filter will exclude items that match *any* of the values in the array.
* When a string is used, exact value comparison is done.
*/
exclude?: string | RegExp | (string | RegExp)[];
}

export interface ContextValueFilterableTreeNodeV2 {
readonly quickPickOptions: {
readonly contextValues: Array<string>;
readonly isLeaf: boolean;
}
}

export type ContextValueFilterableTreeNode = ContextValueFilterableTreeNodeV2 | AzExtTreeItem;

// temporary type until we have the real type from RGs
export type ResourceGroupsItem = ContextValueFilterableTreeNode;
alexweininger marked this conversation as resolved.
Show resolved Hide resolved
21 changes: 21 additions & 0 deletions utils/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { CancellationToken, CancellationTokenSource, Disposable, Event, Extensio
import { TargetPopulation } from 'vscode-tas-client';
import { AzureExtensionApi, AzureExtensionApiProvider } from './api';
import type { Activity, ActivityTreeItemOptions, AppResource, OnErrorActivityData, OnProgressActivityData, OnStartActivityData, OnSuccessActivityData } from './hostapi'; // This must remain `import type` or else a circular reference will result
import type { Box, ContextValueFilter, ResourceGroupsItem, TreeNodeCommandCallback } from './hostapi.v2';

export declare interface RunWithTemporaryDescriptionOptions {
description: string;
Expand Down Expand Up @@ -1678,3 +1679,23 @@ export declare enum AzExtResourceType {
VirtualNetworks = 'VirtualNetworks',
WebHostingEnvironments = 'WebHostingEnvironments',
}

/**
* Tests to see if something is a box, by ensuring it is an object
* and has an "unwrap" function
* @param maybeBox An object to test if it is a box
* @returns True if a box, false otherwise
*/
export declare function isBox(maybeBox: unknown): maybeBox is Box;

export declare function appResourceExperience<TPick>(context: IActionContext, tdp: TreeDataProvider<ResourceGroupsItem>, resourceType: AzExtResourceType, childItemFilter?: ContextValueFilter): Promise<TPick>;

/**
* Used to register VSCode tree node context menu commands that are in the host extension's tree. It wraps your callback with consistent error and telemetry handling
* Use debounce property if you need a delay between clicks for this particular command
* A telemetry event is automatically sent whenever a command is executed. The telemetry event ID will default to the same as the
* commandId passed in, but can be overridden per command with telemetryId
* The telemetry event for this command will be named telemetryId if specified, otherwise it defaults to the commandId
* NOTE: If the environment variable `DEBUGTELEMETRY` is set to a non-empty, non-zero value, then telemetry will not be sent. If the value is 'verbose' or 'v', telemetry will be displayed in the console window.
*/
export declare function registerCommandWithTreeNodeUnboxing<T>(commandId: string, callback: TreeNodeCommandCallback<T>, debounce?: number, telemetryId?: string): void;
1 change: 1 addition & 0 deletions utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ export * from './utils/contextUtils';
export * from './activityLog/activities/ExecuteActivity';
export * from './getAzExtResourceType';
export * from './AzExtResourceType';
export * from './treev2/quickPickWizard/experiences/appResourceExperience';
// NOTE: The auto-fix action "source.organizeImports" does weird things with this file, but there doesn't seem to be a way to disable it on a per-file basis so we'll just let it happen
2 changes: 1 addition & 1 deletion utils/src/registerCommandWithTreeNodeUnboxing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function registerCommandWithTreeNodeUnboxing<T>(commandId: string, treeNo
const boxedNodes = maybeNodeBoxArray as Box[];
nodes = [];
for (const n of boxedNodes) {
nodes.push(await n.unwrap<T>())
nodes.push(n.unwrap<T>())
}
} else if (maybeNodeBoxArray && Array.isArray(maybeNodeBoxArray)) {
// Otherwise, assume it is just an array of T's
Expand Down
29 changes: 1 addition & 28 deletions utils/src/treev2/quickPickWizard/ContextValueQuickPickStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as types from '../../../index';
import { GenericQuickPickOptions, GenericQuickPickStep } from './GenericQuickPickStep';
import { isAzExtParentTreeItem } from '../../tree/InternalInterfaces';
import { QuickPickWizardContext } from './QuickPickWizardContext';

/**
* Describes filtering based on context value. Items that pass the filter will
* match at least one of the `include` filters, but none of the `exclude` filters.
*/
export interface ContextValueFilter {
/**
* This filter will include items that match *any* of the values in the array.
* When a string is used, exact value comparison is done.
*/
include: string | RegExp | (string | RegExp)[];

/**
* This filter will exclude items that match *any* of the values in the array.
* When a string is used, exact value comparison is done.
*/
exclude?: string | RegExp | (string | RegExp)[];
}

export interface ContextValueFilterableTreeNodeV2 {
readonly quickPickOptions: {
readonly contextValues: Array<string>;
readonly isLeaf: boolean;
}
}

export type ContextValueFilterableTreeNode = ContextValueFilterableTreeNodeV2 | types.AzExtTreeItem;
import { ContextValueFilter, ContextValueFilterableTreeNode, ContextValueFilterableTreeNodeV2 } from '../../../hostapi.v2';

export interface ContextValueFilterQuickPickOptions extends GenericQuickPickOptions {
contextValueFilter: ContextValueFilter;
Expand Down
3 changes: 2 additions & 1 deletion utils/src/treev2/quickPickWizard/FindByIdQuickPickStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import * as types from '../../../index';
import * as vscode from 'vscode';
import { getLastNode, QuickPickWizardContext } from './QuickPickWizardContext';
import { isAzExtParentTreeItem } from '../../tree/InternalInterfaces';
import { ContextValueFilterableTreeNodeV2, isContextValueFilterableTreeNodeV2 } from './ContextValueQuickPickStep';
import { isContextValueFilterableTreeNodeV2 } from './ContextValueQuickPickStep';
import { GenericQuickPickStep, SkipIfOneQuickPickOptions } from './GenericQuickPickStep';
import { ContextValueFilterableTreeNodeV2 } from '../../../hostapi.v2';

interface FindableByIdTreeNodeV2 extends ContextValueFilterableTreeNodeV2 {
id: vscode.Uri;
Expand Down
3 changes: 2 additions & 1 deletion utils/src/treev2/quickPickWizard/QuickPickWithCreateStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@

import * as types from '../../../index';
import { getLastNode, QuickPickWizardContext } from './QuickPickWizardContext';
import { ContextValueFilterableTreeNode, ContextValueFilterQuickPickOptions, ContextValueQuickPickStep } from './ContextValueQuickPickStep';
import { ContextValueFilterQuickPickOptions, ContextValueQuickPickStep } from './ContextValueQuickPickStep';
import { localize } from '../../localize';
import { ContextValueFilterableTreeNode } from '../../../hostapi.v2';

type CreateCallback = <TNode extends ContextValueFilterableTreeNode>() => TNode | Promise<TNode>;
interface CreateQuickPickOptions extends ContextValueFilterQuickPickOptions {
Expand Down
3 changes: 2 additions & 1 deletion utils/src/treev2/quickPickWizard/RecursiveQuickPickStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { ContextValueFilterableTreeNode } from '../../../hostapi.v2';
import * as types from '../../../index';
import { ContextValueFilterableTreeNode, ContextValueFilterQuickPickOptions, ContextValueQuickPickStep } from './ContextValueQuickPickStep';
import { ContextValueFilterQuickPickOptions, ContextValueQuickPickStep } from './ContextValueQuickPickStep';
import { getLastNode, QuickPickWizardContext } from './QuickPickWizardContext';

export class RecursiveQuickPickStep<TNode extends ContextValueFilterableTreeNode, TContext extends QuickPickWizardContext<TNode>> extends ContextValueQuickPickStep<TNode, TContext, ContextValueFilterQuickPickOptions> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as types from '../../../../index';
import * as vscode from 'vscode';
import { ContextValueFilter } from '../ContextValueQuickPickStep';
import { QuickPickAzureSubscriptionStep } from '../quickPickAzureResource/QuickPickAzureSubscriptionStep';
import { QuickPickGroupStep } from '../quickPickAzureResource/QuickPickGroupStep';
import { QuickPickAppResourceStep } from '../quickPickAzureResource/QuickPickAppResourceStep';
import { AzureResourceQuickPickWizardContext } from '../quickPickAzureResource/AzureResourceQuickPickWizardContext';
import { RecursiveQuickPickStep } from '../RecursiveQuickPickStep';
import { Box, ResourceGroupsItem } from '../quickPickAzureResource/tempTypes';
import { getLastNode } from '../QuickPickWizardContext';
import { NoResourceFoundError } from '../../../errors';

export async function appResourceExperience<TPick>(context: types.IActionContext, tdp: vscode.TreeDataProvider<ResourceGroupsItem>, resourceType: types.AzExtResourceType, childItemFilter?: ContextValueFilter): Promise<TPick> {
const promptSteps: types.AzureWizardPromptStep<AzureResourceQuickPickWizardContext>[] = [
import { IActionContext } from '../../../../index';
import { AzureWizardPromptStep } from '../../../wizard/AzureWizardPromptStep';
import { AzExtResourceType } from '../../../AzExtResourceType';
import { AzureWizard } from '../../../wizard/AzureWizard';
import { ContextValueFilter, ResourceGroupsItem } from '../../../../hostapi.v2';
import { isBox } from '../../../registerCommandWithTreeNodeUnboxing';

export async function appResourceExperience<TPick>(context: IActionContext, tdp: vscode.TreeDataProvider<ResourceGroupsItem>, resourceType: AzExtResourceType, childItemFilter?: ContextValueFilter): Promise<TPick> {
const promptSteps: AzureWizardPromptStep<AzureResourceQuickPickWizardContext>[] = [
new QuickPickAzureSubscriptionStep(tdp),
new QuickPickGroupStep(tdp, {
groupType: resourceType,
Expand All @@ -38,7 +41,7 @@ export async function appResourceExperience<TPick>(context: types.IActionContext
const wizardContext = context as AzureResourceQuickPickWizardContext;
wizardContext.pickedNodes = [];

const wizard = new types.AzureWizard(context, {
const wizard = new AzureWizard(context, {
hideStepCount: true,
promptSteps: promptSteps,
});
Expand All @@ -50,9 +53,6 @@ export async function appResourceExperience<TPick>(context: types.IActionContext
if (!lastPickedItem) {
throw new NoResourceFoundError(wizardContext);
} else {
// Treat lastPickedItem as a box containing the desired end object
// TODO
const pickedAsBox = lastPickedItem as unknown as Box<TPick>;
return pickedAsBox.unwrap();
return isBox(lastPickedItem) ? lastPickedItem.unwrap<TPick>() : lastPickedItem as unknown as TPick;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as types from '../../../../index';
import * as vscode from 'vscode';
import { ContextValueFilter, ContextValueFilterableTreeNode } from '../ContextValueQuickPickStep';
import { RecursiveQuickPickStep } from '../RecursiveQuickPickStep';
import { getLastNode, QuickPickWizardContext } from '../QuickPickWizardContext';
import { NoResourceFoundError } from '../../../errors';
import { AzureWizardPromptStep } from '../../../wizard/AzureWizardPromptStep';
import { IActionContext } from '../../../../index';
import { AzureWizard } from '../../../wizard/AzureWizard';
import { ContextValueFilter, ContextValueFilterableTreeNode } from '../../../../hostapi.v2';

export async function contextValueExperience<TPick extends ContextValueFilterableTreeNode>(context: types.IActionContext, tdp: vscode.TreeDataProvider<TPick>, contextValueFilter: ContextValueFilter): Promise<TPick> {
const promptSteps: types.AzureWizardPromptStep<QuickPickWizardContext<TPick>>[] = [
export async function contextValueExperience<TPick extends ContextValueFilterableTreeNode>(context: IActionContext, tdp: vscode.TreeDataProvider<TPick>, contextValueFilter: ContextValueFilter): Promise<TPick> {
const promptSteps: AzureWizardPromptStep<QuickPickWizardContext<TPick>>[] = [
new RecursiveQuickPickStep(tdp, {
contextValueFilter: contextValueFilter,
skipIfOne: false,
Expand All @@ -22,7 +24,7 @@ export async function contextValueExperience<TPick extends ContextValueFilterabl
const wizardContext = context as QuickPickWizardContext<TPick>;
wizardContext.pickedNodes = [];

const wizard = new types.AzureWizard(context, {
const wizard = new AzureWizard(context, {
hideStepCount: true,
promptSteps: promptSteps,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { ApplicationResource, ApplicationSubscription, ResourceGroupsItem } from "../../../../hostapi.v2";
import { QuickPickWizardContext } from "../QuickPickWizardContext";
import { ApplicationResource, ApplicationSubscription, ResourceGroupsItem } from "./tempTypes";

export interface AzureResourceQuickPickWizardContext extends QuickPickWizardContext<ResourceGroupsItem> {
subscription: ApplicationSubscription | undefined;
resource: ApplicationResource | undefined;
resourceGroup: string | undefined;
subscription?: ApplicationSubscription;
resource?: ApplicationResource;
resourceGroup?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { ContextValueFilter, ResourceGroupsItem } from '../../../../hostapi.v2';
import * as types from '../../../../index';
import { ContextValueFilter } from '../ContextValueQuickPickStep';
import { GenericQuickPickOptions, GenericQuickPickStep } from '../GenericQuickPickStep';
import { AzureResourceQuickPickWizardContext } from './AzureResourceQuickPickWizardContext';
import { AppResourceItem, ResourceGroupsItem } from './tempTypes';
import { AppResourceItem } from './tempTypes';

interface AppResourceQuickPickOptions extends GenericQuickPickOptions {
resourceType: types.AzExtResourceType;
Expand All @@ -19,8 +19,8 @@ export class QuickPickAppResourceStep extends GenericQuickPickStep<ResourceGroup
const pickedAppResource = await super.promptInternal(wizardContext) as AppResourceItem;

// TODO
wizardContext.resource = pickedAppResource;
wizardContext.resourceGroup = pickedAppResource.resourceGroup;
wizardContext.resource = pickedAppResource.resource;
wizardContext.resourceGroup = pickedAppResource.resource.resourceGroup;

return pickedAppResource;
}
Expand All @@ -31,7 +31,7 @@ export class QuickPickAppResourceStep extends GenericQuickPickStep<ResourceGroup
return false;
}

return node.azExtResourceType === this.pickOptions.resourceType;
return node.resource.azExtResourceType === this.pickOptions.resourceType;
}

protected isIndirectPick(node: AppResourceItem): boolean {
Expand All @@ -40,6 +40,6 @@ export class QuickPickAppResourceStep extends GenericQuickPickStep<ResourceGroup
return false;
}

return node.azExtResourceType === this.pickOptions.resourceType;
return node.resource.azExtResourceType === this.pickOptions.resourceType;
}
}
Loading