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

Add compatibility steps #1235

Merged
merged 34 commits into from
Sep 15, 2022
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
6177178
Fixup types
alexweininger Sep 9, 2022
f3c1edf
Allow showing all app resources
alexweininger Sep 9, 2022
976c2f5
Support create in `GenericQuickPickStep`
alexweininger Sep 9, 2022
c83969a
pickTreeItemImpl compatability
alexweininger Sep 9, 2022
f089c5a
Fix loading prompt cancel issue
alexweininger Sep 9, 2022
0696f83
Fix create pick issue
alexweininger Sep 9, 2022
068f811
Comments
alexweininger Sep 9, 2022
9cf2d37
Put compatibility code into separate steps
alexweininger Sep 13, 2022
6f30693
Fixups
alexweininger Sep 14, 2022
d92870c
Undo changes
alexweininger Sep 14, 2022
1bd382d
Undo changes
alexweininger Sep 14, 2022
f65de1a
Use interfaces
alexweininger Sep 14, 2022
80ce9b9
Add back QuickPickWithCreateStep
alexweininger Sep 14, 2022
0dd1815
Merge remote-tracking branch 'origin/bmw/quickPick_v8.30' into alex/t…
alexweininger Sep 14, 2022
815ddfa
Revert changes to QuickPickWithCreateStep.ts
alexweininger Sep 14, 2022
a16d47d
Fixups
alexweininger Sep 14, 2022
50f4e82
Fixup
alexweininger Sep 14, 2022
651f894
unboxing -> unwrapping
alexweininger Sep 14, 2022
df2c874
Fixups
alexweininger Sep 14, 2022
1010243
Use options
alexweininger Sep 14, 2022
c572cb3
Remove unused import
alexweininger Sep 14, 2022
e13eea3
Fixup
alexweininger Sep 14, 2022
4f1923e
Remove unneeded compat logic
alexweininger Sep 14, 2022
9969dc4
Cleanup
alexweininger Sep 14, 2022
f6a2409
Add missing semicolon
alexweininger Sep 14, 2022
a9ed674
Nested ternary formatting
alexweininger Sep 14, 2022
fc51c5b
Update utils/index.d.ts
alexweininger Sep 14, 2022
94aa274
Remove `ContextValueFilterableTreeNodeV2`
alexweininger Sep 14, 2022
d91bf8d
TPick extends AzExtTreeItem
alexweininger Sep 14, 2022
074448c
Add `isAzExtTreeItem` function
alexweininger Sep 14, 2022
071fb87
Catch and ignore NoResourceFoundError
alexweininger Sep 14, 2022
91ea48c
Ensure maybeTreeItem is an object
alexweininger Sep 14, 2022
76d677e
Fixup
alexweininger Sep 14, 2022
fa1e79a
Swallow NoResourceFoundError in QuickPickWithCreateStep
alexweininger Sep 14, 2022
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
3 changes: 1 addition & 2 deletions utils/hostapi.v2.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

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

export declare interface ApplicationAuthentication {
getSession(scopes?: string[]): vscode.ProviderResult<vscode.AuthenticationSession>;
Expand Down
43 changes: 34 additions & 9 deletions utils/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

import type { Environment } from '@azure/ms-rest-azure-env';
import { CancellationToken, CancellationTokenSource, Disposable, Event, ExtensionContext, FileChangeEvent, FileChangeType, FileStat, FileSystemProvider, FileType, InputBoxOptions, MarkdownString, MessageItem, MessageOptions, OpenDialogOptions, OutputChannel, Progress, QuickPickItem, QuickPickOptions, TextDocumentShowOptions, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, TreeView, Uri } from 'vscode';
import { CancellationToken, CancellationTokenSource, Disposable, Event, ExtensionContext, FileChangeEvent, FileChangeType, FileStat, FileSystemProvider, FileType, InputBoxOptions, MarkdownString, MessageItem, MessageOptions, OpenDialogOptions, OutputChannel, Progress, QuickPickItem, QuickPickOptions as VSCodeQuickPickOptions, TextDocumentShowOptions, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, TreeView, Uri } from 'vscode';
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
Expand Down Expand Up @@ -360,6 +360,7 @@ export interface IAzExtParentTreeItem extends IAzExtTreeItem {
* NOTE: *Impl methods are not meant to be called directly - just implemented.
*/
export declare abstract class AzExtTreeItem implements IAzExtTreeItem {
public readonly _isAzExtTreeItem = true;
bwateratmsft marked this conversation as resolved.
Show resolved Hide resolved
//#region Properties implemented by base class
/**
* This is is used for the openInPortal action. It is also used per the following documentation copied from VS Code:
Expand Down Expand Up @@ -993,7 +994,7 @@ export interface IAzureQuickPickItem<T = undefined> extends QuickPickItem {
/**
* Provides additional options for QuickPicks used in Azure Extensions
*/
export interface IAzureQuickPickOptions extends QuickPickOptions, AzExtUserInputOptions {
export interface IAzureQuickPickOptions extends VSCodeQuickPickOptions, AzExtUserInputOptions {
/**
* An optional id to identify this QuickPick across sessions, used in persisting previous selections
* If not specified, a hash of the placeHolder will be used
Expand Down Expand Up @@ -1703,10 +1704,17 @@ export declare interface Wrapper {
*/
export declare function isWrapper(maybeWrapper: unknown): maybeWrapper is Wrapper;

export declare function appResourceExperience<TPick extends ContextValueFilterableTreeNode>(context: IActionContext, tdp: TreeDataProvider<ResourceGroupsItem>, resourceType: AzExtResourceType, childItemFilter?: ContextValueFilter): Promise<TPick>;
export declare function appResourceExperience<TPick extends ContextValueFilterableTreeNode>(context: IActionContext, tdp: TreeDataProvider<ResourceGroupsItem>, resourceTypes?: AzExtResourceType | AzExtResourceType[], childItemFilter?: ContextValueFilter): Promise<TPick>;
export declare function contextValueExperience<TPick extends ContextValueFilterableTreeNode>(context: IActionContext, tdp: TreeDataProvider<ResourceGroupsItem>, contextValueFilter: ContextValueFilter): Promise<TPick>;
alexweininger marked this conversation as resolved.
Show resolved Hide resolved
export declare function findByIdExperience<TPick extends FindableByIdTreeNode>(context: IActionContext, tdp: TreeDataProvider<TPick>, id: string | Uri): Promise<TPick>;

interface CompatibilityPickResourceExperienceOptions {
resourceTypes?: AzExtResourceType | AzExtResourceType[];
childItemFilter?: ContextValueFilter
}

export declare function compatibilityPickAppResourceExperience<TPick>(context: IActionContext, tdp: TreeDataProvider<ResourceGroupsItem>, options: CompatibilityPickResourceExperienceOptions): Promise<TPick>;
alexweininger marked this conversation as resolved.
Show resolved Hide resolved

export declare interface QuickPickWizardContext<TNode extends unknown> extends IActionContext {
pickedNodes: TNode[];
}
Expand All @@ -1729,14 +1737,31 @@ export declare interface ContextValueFilter {
exclude?: string | RegExp | (string | RegExp)[];
}

interface QuickPickOptions {
readonly contextValues: Array<string>;
readonly isLeaf: boolean;
}

type CreateCallback<TNode = unknown> = (context: IActionContext) => TNode | Promise<TNode>;

type CreateOptions<TNode = unknown> = {
label?: string;
callback: CreateCallback<TNode>;
}

interface CompatibleQuickPickOptions extends QuickPickOptions {
readonly createChild?: CreateOptions;
}

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

export declare interface CompatibleContextValueFilterableTreeNode {
readonly quickPickOptions: CompatibleQuickPickOptions;
}

export declare type ContextValueFilterableTreeNode = ContextValueFilterableTreeNodeV2 | AzExtTreeItem;
export declare type ContextValueFilterableTreeNode = ContextValueFilterableTreeNodeV2;
alexweininger marked this conversation as resolved.
Show resolved Hide resolved

export declare interface FindableByIdTreeNodeV2 extends ContextValueFilterableTreeNodeV2 {
id: string;
Expand All @@ -1752,4 +1777,4 @@ export declare type FindableByIdTreeNode = FindableByIdTreeNodeV2 | AzExtTreeIte
* 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;
export declare function registerCommandWithTreeNodeUnwrapping<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 @@ -38,6 +38,7 @@ export * from './activityLog/activities/ExecuteActivity';
export * from './getAzExtResourceType';
export * from './AzExtResourceType';
export * from './treev2/quickPickWizard/experiences/appResourceExperience';
export * from './treev2/quickPickWizard/experiences/compatibilityPickResourceExperience';
export * from './treev2/quickPickWizard/experiences/contextValueExperience';
export * from './treev2/quickPickWizard/experiences/findByIdExperience';
// 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
5 changes: 4 additions & 1 deletion utils/src/tree/AzExtTreeItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import { settingUtils } from '../utils/settingUtils';
import { showContextValueSetting } from '../constants';

export abstract class AzExtTreeItem implements types.AzExtTreeItem {

public readonly _isAzExtTreeItem = true;

//#region Properties implemented by base class
public abstract label: string;
public abstract contextValue: string;
Expand Down Expand Up @@ -120,7 +123,7 @@ export abstract class AzExtTreeItem implements types.AzExtTreeItem {
}

public get tooltip(): string | undefined {
if(process.env.DEBUGTELEMETRY === 'v' && !!settingUtils.getWorkspaceSetting<unknown>(showContextValueSetting)) {
if (process.env.DEBUGTELEMETRY === 'v' && !!settingUtils.getWorkspaceSetting<unknown>(showContextValueSetting)) {
return `Context: "${this.contextValue}"`;
} else {
return this._tooltip;
Expand Down
23 changes: 2 additions & 21 deletions utils/src/treev2/quickPickWizard/ContextValueQuickPickStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

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

export interface ContextValueFilterQuickPickOptions extends GenericQuickPickOptions {
contextValueFilter: types.ContextValueFilter;
Expand All @@ -21,22 +20,14 @@ export class ContextValueQuickPickStep<TNode extends types.ContextValueFilterabl
(Array.isArray(excludeOption) ? excludeOption : [excludeOption]) :
[];

const nodeContextValues: string[] = isContextValueFilterableTreeNodeV2(node) ?
node.quickPickOptions.contextValues :
[node.contextValue];
const nodeContextValues: string[] = node.quickPickOptions.contextValues;

return includeArray.some(i => this.matchesSingleFilter(i, nodeContextValues)) &&
!excludeArray.some(e => this.matchesSingleFilter(e, nodeContextValues));
}

protected override isIndirectPick(node: TNode): boolean {
if (isContextValueFilterableTreeNodeV2(node)) {
return node.quickPickOptions.isLeaf === false;
} else if (isAzExtParentTreeItem(node)) {
return true;
}

return false;
return node.quickPickOptions.isLeaf === false;
}

private matchesSingleFilter(matcher: string | RegExp, nodeContextValues: string[]): boolean {
Expand All @@ -50,13 +41,3 @@ export class ContextValueQuickPickStep<TNode extends types.ContextValueFilterabl
})
}
}

export function isContextValueFilterableTreeNodeV2(maybeNode: unknown): maybeNode is types.ContextValueFilterableTreeNodeV2 {
if (typeof maybeNode === 'object') {
return Array.isArray((maybeNode as types.ContextValueFilterableTreeNodeV2).quickPickOptions?.contextValues) &&
(maybeNode as types.ContextValueFilterableTreeNodeV2).quickPickOptions?.isLeaf !== undefined &&
(maybeNode as types.ContextValueFilterableTreeNodeV2).quickPickOptions?.isLeaf !== null;
}

return false;
}
11 changes: 10 additions & 1 deletion utils/src/treev2/quickPickWizard/FindByIdQuickPickStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import * as types from '../../../index';
import * as vscode from 'vscode';
import { getLastNode } from './QuickPickWizardContext';
import { isAzExtParentTreeItem } from '../../tree/InternalInterfaces';
import { isContextValueFilterableTreeNodeV2 } from './ContextValueQuickPickStep';
import { GenericQuickPickStep, SkipIfOneQuickPickOptions } from './GenericQuickPickStep';

interface FindByIdQuickPickOptions extends SkipIfOneQuickPickOptions {
Expand Down Expand Up @@ -76,6 +75,16 @@ export class FindByIdQuickPickStep<TNode extends types.FindableByIdTreeNode, TCo
}
}

function isContextValueFilterableTreeNodeV2(maybeNode: unknown): maybeNode is types.ContextValueFilterableTreeNodeV2 {
if (typeof maybeNode === 'object') {
return Array.isArray((maybeNode as types.ContextValueFilterableTreeNodeV2).quickPickOptions?.contextValues) &&
(maybeNode as types.ContextValueFilterableTreeNodeV2).quickPickOptions?.isLeaf !== undefined &&
(maybeNode as types.ContextValueFilterableTreeNodeV2).quickPickOptions?.isLeaf !== null;
}

return false;
}

function isFindableByIdTreeNodeV2(maybeNode: unknown): maybeNode is types.FindableByIdTreeNodeV2 {
if (!isContextValueFilterableTreeNodeV2(maybeNode)) {
return false;
Expand Down
8 changes: 6 additions & 2 deletions utils/src/treev2/quickPickWizard/GenericQuickPickStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ export abstract class GenericQuickPickStep<TNode extends unknown, TContext exten
if (picks.length === 1 && this.pickOptions.skipIfOne) {
return picks[0].data;
} else {
const selected = await wizardContext.ui.showQuickPick(picks, { /* TODO: options */ });
const selected = await wizardContext.ui.showQuickPick(picks, {
/* TODO: options */
/* TODO: set id here so recently picked items appear at the top */
});

return selected.data;
}
}
Expand Down Expand Up @@ -100,7 +104,7 @@ export abstract class GenericQuickPickStep<TNode extends unknown, TContext exten
*/
protected abstract isIndirectPick(node: TNode): boolean;

private async getQuickPickItem(resource: TNode): Promise<types.IAzureQuickPickItem<TNode>> {
protected async getQuickPickItem(resource: TNode): Promise<types.IAzureQuickPickItem<TNode>> {
const treeItem = await Promise.resolve(this.treeDataProvider.getTreeItem(resource));

return {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as types from "../../../../index";
import { isAzExtParentTreeItem } from "../../../tree/InternalInterfaces";
import { ContextValueFilterQuickPickOptions, ContextValueQuickPickStep } from "../ContextValueQuickPickStep";
import { getLastNode } from "../QuickPickWizardContext";
import { AzExtTreeItem } from "../../../tree/AzExtTreeItem";
import { AzExtParentTreeItem } from "../../../tree/AzExtParentTreeItem";
import { isWrapper } from "../../../registerCommandWithTreeNodeUnwrapping";

/**
* Provides compatability with {@link AzExtParentTreeItem.pickTreeItemImpl}
*/
export class CompatibilityContextValueQuickPickStep<TNode extends types.CompatibleContextValueFilterableTreeNode, TContext extends types.QuickPickWizardContext<TNode>, TOptions extends ContextValueFilterQuickPickOptions> extends ContextValueQuickPickStep<TNode, TContext, TOptions> {

public override async prompt(wizardContext: TContext): Promise<void> {
await this.provideCompatabilityWithPickTreeItemImpl(wizardContext) || await super.prompt(wizardContext);
}

/**
* Mimics how the legacy {@link AzExtParentTreeItem.pickChildTreeItem}
* uses {@link AzExtParentTreeItem.pickTreeItemImpl} to customize the tree item picker.
*
* An example customization is skipping having to pick a UI-only node (ex: App Settings parent node)
*/
private async provideCompatabilityWithPickTreeItemImpl(wizardContext: TContext): Promise<boolean> {
const lastPickedItem = getLastNode(wizardContext);
const lastPickedItemUnwrapped = isWrapper(lastPickedItem) ? lastPickedItem.unwrap() : lastPickedItem
alexweininger marked this conversation as resolved.
Show resolved Hide resolved
if (isAzExtParentTreeItem(lastPickedItemUnwrapped)) {
const children = await this.treeDataProvider.getChildren(lastPickedItem);
if (children && children.length) {
const customChild = await this.getCustomChildren(wizardContext, lastPickedItemUnwrapped);

const customPick = children.find((child) => {
const ti: AzExtTreeItem = isWrapper(child) ? child.unwrap() : child as unknown as AzExtTreeItem;
return ti.fullId === customChild?.fullId;
});

if (customPick) {
wizardContext.pickedNodes.push(customPick);
return true;
}
}
}
return false;
}

private async getCustomChildren(context: TContext, node: AzExtParentTreeItem): Promise<AzExtTreeItem | undefined> {
return await node.pickTreeItemImpl?.(Array.isArray(this.pickOptions.contextValueFilter.include) ? this.pickOptions.contextValueFilter.include : [this.pickOptions.contextValueFilter.include], context);
}
}
Loading