diff --git a/azure/api-extractor.json b/azure/api-extractor.json new file mode 100644 index 0000000000..67e6244bcb --- /dev/null +++ b/azure/api-extractor.json @@ -0,0 +1,437 @@ +/** + * Config file for API Extractor. For more info, please visit: https://api-extractor.com + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + /** + * Optionally specifies another JSON config file that this file extends from. This provides a way for + * standard settings to be shared across multiple projects. + * + * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains + * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be + * resolved using NodeJS require(). + * + * SUPPORTED TOKENS: none + * DEFAULT VALUE: "" + */ + // "extends": "./shared/api-extractor-base.json" + // "extends": "my-package/include/api-extractor-base.json" + /** + * Determines the "" token that can be used with other config file settings. The project folder + * typically contains the tsconfig.json and package.json config files, but the path is user-defined. + * + * The path is resolved relative to the folder of the config file that contains the setting. + * + * The default value for "projectFolder" is the token "", which means the folder is determined by traversing + * parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder + * that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error + * will be reported. + * + * SUPPORTED TOKENS: + * DEFAULT VALUE: "" + */ + // "projectFolder": "..", + /** + * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor + * analyzes the symbols exported by this module. + * + * The file extension must be ".d.ts" and not ".ts". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + */ + "mainEntryPointFilePath": "/dist/esm/src/index.d.ts", + /** + * A list of NPM package names whose exports should be treated as part of this package. + * + * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1", + * and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part + * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly + * imports library2. To avoid this, we might specify: + * + * "bundledPackages": [ "library2" ], + * + * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been + * local files for library1. + * + * The "bundledPackages" elements may specify glob patterns using minimatch syntax. To ensure deterministic + * output, globs are expanded by matching explicitly declared top-level dependencies only. For example, + * the pattern below will NOT match "@my-company/example" unless it appears in a field such as "dependencies" + * or "devDependencies" of the project's package.json file: + * + * "bundledPackages": [ "@my-company/*" ], + */ + "bundledPackages": [], + /** + * Specifies what type of newlines API Extractor should use when writing output files. By default, the output files + * will be written with Windows-style newlines. To use POSIX-style newlines, specify "lf" instead. + * To use the OS's default newline kind, specify "os". + * + * DEFAULT VALUE: "crlf" + */ + // "newlineKind": "crlf", + /** + * Specifies how API Extractor sorts members of an enum when generating the .api.json file. By default, the output + * files will be sorted alphabetically, which is "by-name". To keep the ordering in the source code, specify + * "preserve". + * + * DEFAULT VALUE: "by-name" + */ + // "enumMemberOrder": "by-name", + /** + * Set to true when invoking API Extractor's test harness. When `testMode` is true, the `toolVersion` field in the + * .api.json file is assigned an empty string to prevent spurious diffs in output files tracked for tests. + * + * DEFAULT VALUE: "false" + */ + // "testMode": false, + /** + * Determines how the TypeScript compiler engine will be invoked by API Extractor. + */ + "compiler": { + /** + * Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * Note: This setting will be ignored if "overrideTsconfig" is used. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/tsconfig.json" + */ + // "tsconfigFilePath": "/tsconfig.json", + /** + * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk. + * The object must conform to the TypeScript tsconfig schema: + * + * http://json.schemastore.org/tsconfig + * + * If omitted, then the tsconfig.json file will be read from the "projectFolder". + * + * DEFAULT VALUE: no overrideTsconfig section + */ + // "overrideTsconfig": { + // . . . + // } + /** + * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended + * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when + * dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses + * for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck. + * + * DEFAULT VALUE: false + */ + // "skipLibCheck": true, + }, + /** + * Configures how the API report file (*.api.md) will be generated. + */ + "apiReport": { + /** + * (REQUIRED) Whether to generate an API report. + */ + "enabled": false + /** + * The base filename for the API report files, to be combined with "reportFolder" or "reportTempFolder" + * to produce the full file path. The "reportFileName" should not include any path separators such as + * "\" or "/". The "reportFileName" should not include a file extension, since API Extractor will automatically + * append an appropriate file extension such as ".api.md". If the "reportVariants" setting is used, then the + * file extension includes the variant name, for example "my-report.public.api.md" or "my-report.beta.api.md". + * The "complete" variant always uses the simple extension "my-report.api.md". + * + * Previous versions of API Extractor required "reportFileName" to include the ".api.md" extension explicitly; + * for backwards compatibility, that is still accepted but will be discarded before applying the above rules. + * + * SUPPORTED TOKENS: , + * DEFAULT VALUE: "" + */ + // "reportFileName": "", + /** + * To support different approval requirements for different API levels, multiple "variants" of the API report can + * be generated. The "reportVariants" setting specifies a list of variants to be generated. If omitted, + * by default only the "complete" variant will be generated, which includes all @internal, @alpha, @beta, + * and @public items. Other possible variants are "alpha" (@alpha + @beta + @public), "beta" (@beta + @public), + * and "public" (@public only). + * + * DEFAULT VALUE: [ "complete" ] + */ + // "reportVariants": ["public", "beta"], + /** + * Specifies the folder where the API report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy, + * e.g. for an API review. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/etc/" + */ + // "reportFolder": "/etc/", + /** + * Specifies the folder where the temporary report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * After the temporary file is written to disk, it is compared with the file in the "reportFolder". + * If they are different, a production build will fail. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/" + */ + // "reportTempFolder": "/temp/", + /** + * Whether "forgotten exports" should be included in the API report file. Forgotten exports are declarations + * flagged with `ae-forgotten-export` warnings. See https://api-extractor.com/pages/messages/ae-forgotten-export/ to + * learn more. + * + * DEFAULT VALUE: "false" + */ + // "includeForgottenExports": false + }, + /** + * Configures how the doc model file (*.api.json) will be generated. + */ + "docModel": { + /** + * (REQUIRED) Whether to generate a doc model file. + */ + "enabled": true + /** + * The output path for the doc model file. The file extension should be ".api.json". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/.api.json" + */ + // "apiJsonFilePath": "/temp/.api.json", + /** + * Whether "forgotten exports" should be included in the doc model file. Forgotten exports are declarations + * flagged with `ae-forgotten-export` warnings. See https://api-extractor.com/pages/messages/ae-forgotten-export/ to + * learn more. + * + * DEFAULT VALUE: "false" + */ + // "includeForgottenExports": false, + /** + * The base URL where the project's source code can be viewed on a website such as GitHub or + * Azure DevOps. This URL path corresponds to the `` path on disk. + * + * This URL is concatenated with the file paths serialized to the doc model to produce URL file paths to individual API items. + * For example, if the `projectFolderUrl` is "https://github.com/microsoft/rushstack/tree/main/apps/api-extractor" and an API + * item's file path is "api/ExtractorConfig.ts", the full URL file path would be + * "https://github.com/microsoft/rushstack/tree/main/apps/api-extractor/api/ExtractorConfig.js". + * + * This setting can be omitted if you don't need source code links in your API documentation reference. + * + * SUPPORTED TOKENS: none + * DEFAULT VALUE: "" + */ + // "projectFolderUrl": "http://github.com/path/to/your/projectFolder" + }, + /** + * Configures how the .d.ts rollup file will be generated. + */ + "dtsRollup": { + /** + * (REQUIRED) Whether to generate the .d.ts rollup file. + */ + "enabled": true + /** + * Specifies the output path for a .d.ts rollup file to be generated without any trimming. + * This file will include all declarations that are exported by the main entry point. + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/dist/.d.ts" + */ + // "untrimmedFilePath": "/dist/.d.ts", + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for an "alpha" release. + * This file will include only declarations that are marked as "@public", "@beta", or "@alpha". + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "alphaTrimmedFilePath": "/dist/-alpha.d.ts", + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release. + * This file will include only declarations that are marked as "@public" or "@beta". + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "betaTrimmedFilePath": "/dist/-beta.d.ts", + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release. + * This file will include only declarations that are marked as "@public". + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "publicTrimmedFilePath": "/dist/-public.d.ts", + /** + * When a declaration is trimmed, by default it will be replaced by a code comment such as + * "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the + * declaration completely. + * + * DEFAULT VALUE: false + */ + // "omitTrimmingComments": true + }, + /** + * Configures how the tsdoc-metadata.json file will be generated. + */ + "tsdocMetadata": { + /** + * Whether to generate the tsdoc-metadata.json file. + * + * DEFAULT VALUE: true + */ + // "enabled": true, + /** + * Specifies where the TSDoc metadata file should be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * The default value is "", which causes the path to be automatically inferred from the "tsdocMetadata", + * "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup + * falls back to "tsdoc-metadata.json" in the package folder. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json" + }, + /** + * Configures how API Extractor reports error and warning messages produced during analysis. + * + * There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages. + */ + "messages": { + /** + * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing + * the input .d.ts files. + * + * TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "compilerMessageReporting": { + /** + * Configures the default routing for messages that don't match an explicit rule in this table. + */ + "default": { + /** + * Specifies whether the message should be written to the the tool's output log. Note that + * the "addToApiReportFile" property may supersede this option. + * + * Possible values: "error", "warning", "none" + * + * Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail + * and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes + * the "--local" option), the warning is displayed but the build will not fail. + * + * DEFAULT VALUE: "warning" + */ + "logLevel": "warning" + /** + * When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md), + * then the message will be written inside that file; otherwise, the message is instead logged according to + * the "logLevel" option. + * + * DEFAULT VALUE: false + */ + // "addToApiReportFile": false + } + // "TS2551": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + /** + * Configures handling of messages reported by API Extractor during its analysis. + * + * API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag" + * + * DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings + */ + "extractorMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + }, + "ae-missing-release-tag": { + "logLevel": "none" + } + // "ae-extra-release-tag": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + /** + * Configures handling of messages reported by the TSDoc parser when analyzing code comments. + * + * TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "tsdocMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + }, + "tsdoc-param-tag-missing-hyphen": { + "logLevel": "none" + }, + "tsdoc-escape-right-brace": { + "logLevel": "none" + }, + "tsdoc-malformed-inline-tag": { + "logLevel": "none" + }, + "tsdoc-characters-after-block-tag": { + "logLevel": "none" + } + // "tsdoc-link-tag-unescaped-text": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + } + } +} diff --git a/azure/index.d.ts b/azure/index.d.ts deleted file mode 100644 index 522ee85893..0000000000 --- a/azure/index.d.ts +++ /dev/null @@ -1,712 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import { AuthorizationManagementClient, type RoleDefinition } from '@azure/arm-authorization'; -import { Identity, ManagedServiceIdentityClient } from '@azure/arm-msi'; -import type { ExtendedLocation, ResourceGroup } from '@azure/arm-resources'; -import type { Location } from '@azure/arm-resources-subscriptions'; -import type { StorageAccount } from '@azure/arm-storage'; -import { type StorageManagementClient } from '@azure/arm-storage'; -import type { ServiceClient, ServiceClientOptions } from '@azure/core-client'; -import type { PagedAsyncIterableIterator } from '@azure/core-paging'; -import type { PipelineRequestOptions, PipelineResponse } from '@azure/core-rest-pipeline'; -import type { Environment } from '@azure/ms-rest-azure-env'; -import type { AzExtParentTreeItem, AzExtServiceClientCredentials, AzExtServiceClientCredentialsT2, AzExtTreeItem, AzureNameStep, AzureWizardExecuteStep, AzureWizardExecuteStepWithActivityOutput, AzureWizardPromptStep, IActionContext, IAzureNamingRules, IAzureQuickPickItem, IAzureQuickPickOptions, IAzureUserInput, IRelatedNameWizardContext, ISubscriptionActionContext, ISubscriptionContext, IWizardOptions, TreeElementBase, UIExtensionVariables } from '@microsoft/vscode-azext-utils'; -import type { AzureSubscription } from '@microsoft/vscode-azureresources-api'; -import { Disposable, LogOutputChannel, Progress, ProviderResult, TreeItem, Uri } from 'vscode'; - -export type OpenInPortalOptions = { - /** - * A query string applied directly to the host URL, e.g. "feature.staticwebsites=true" (turns on a preview feature) - */ - queryPrefix?: string; -}; - -/** - * Implement this class to display resources under a standard subscription tree item - */ -export declare abstract class SubscriptionTreeItemBase extends AzExtParentTreeItem { - public static readonly contextValue: string; - public readonly contextValue: string; - public readonly label: string; - constructor(parent: AzExtParentTreeItem, subscription: ISubscriptionContext); -} - -/** - * A tree item for an Azure Account, which will display subscriptions. For Azure-centered extensions, this will be at the root of the tree. - */ -export declare abstract class AzureAccountTreeItemBase extends AzExtParentTreeItem implements Disposable { - public static readonly contextValue: string; - public contextValue: string; - public label: string; - public disposables: Disposable[]; - public childTypeLabel: string; - public autoSelectInTreeItemPicker: boolean; - - //#region Methods implemented by base class - /** - * Implement this to create a subscription tree item under this Azure Account node - * @param root Contains basic information about the subscription - should be passed in to the constructor of `SubscriptionTreeItemBase` - */ - public abstract createSubscriptionTreeItem(root: ISubscriptionContext): SubscriptionTreeItemBase | Promise; - //#endregion - - /** - * Azure Account Tree Item - * @param parent The parent of this node or undefined if it's the root of the tree. - * @param testAccount Unofficial api for testing - */ - public constructor(parent?: AzExtParentTreeItem, testAccount?: {}); - - public dispose(): void; - - /** - * If user is logged in and only has one subscription selected, adds that to the wizardContext and returns undefined - * Else, returns a prompt step for a subscription - */ - public getSubscriptionPromptStep(wizardContext: Partial): Promise | undefined>; - - public hasMoreChildrenImpl(): boolean; - public loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise; - public pickTreeItemImpl(expectedContextValues: (string | RegExp)[]): Promise; - public getIsLoggedIn(): Promise; -} - -/** -* Combines the root.environment.portalLink and id to open the resource in the portal. -* -* NOTE: If root is a tree item, it will find the subscription ancestor and get environment.portalLink from there -*/ -export declare function openInPortal(root: ISubscriptionContext | AzExtTreeItem, id: string, options?: OpenInPortalOptions): Promise; - -export type AzExtLocation = Location & { - id: string; - name: string; - displayName: string; -} - -/** - * Currently no location-specific properties on the wizard context, but keeping this interface for backwards compatibility and ease of use - * Instead, use static methods on `LocationListStep` like `getLocation` and `setLocationSubset` - */ -export interface ILocationWizardContext extends ISubscriptionActionContext { - includeExtendedLocations?: boolean; -} - -export declare class LocationListStep extends AzureWizardPromptStep { - protected constructor(); - - /** - * Adds a LocationListStep to the wizard. This function will ensure there is only one LocationListStep per wizard context. - * @param wizardContext The context of the wizard - * @param promptSteps The array of steps to include the LocationListStep to - * @param options Options to pass to ui.showQuickPick. Options are spread onto the defaults. - */ - public static addStep(wizardContext: IActionContext & Partial, promptSteps: AzureWizardPromptStep[], options?: IAzureQuickPickOptions): void; - - /** - * This will set the wizard context's location (in which case the user will _not_ be prompted for location) - * For example, if the user selects an existing resource, you might want to use that location as the default for the wizard's other resources - * This _will_ set the location even if not all providers support it - in the hopes that a related location can be found during `getLocation` - * @param wizardContext The context of the wizard - * @param name The name or display name of the location - */ - public static setLocation(wizardContext: T, name: string): Promise; - - /** - * Sets a location to auto-select during prompting, if available. - * Use this instead of `setLocation` when you want to automatically select a location - * that respects all future resource providers. - * @param wizardContext The context of the wizard - * @param name The name or display name of the suggested location - */ - public static setAutoSelectLocation(wizardContext: T, name: string): Promise; - - /** - * Resets all location and location-related metadata on the wizard context back to its uninitialized state. - * This includes clearing the selected location, cached location tasks, provider location maps, and any step-tracking flags. - * Use this to ensure the wizard context is fully reset before starting a new location selection process. - * @param wizardContext The context of the wizard - */ - public static resetLocation(wizardContext: T): void; - - /** - * Specify a task that will be used to filter locations - * @param wizardContext The context of the wizard - * @param task A task evaluating to the locations supported by this provider - * @param provider The relevant provider (i.e. 'Microsoft.Web') - */ - public static setLocationSubset(wizardContext: T, task: Promise, provider: string): void; - - /** - * Adds default location filtering for a provider - * If more granular filtering is needed, use `setLocationSubset` instead (i.e. if the provider further filters locations based on features) - * @param wizardContext The context of the wizard - * @param provider The provider (i.e. 'Microsoft.Storage') - * @param resourceType The resource type (i.e. 'storageAccounts') - */ - public static addProviderForFiltering(wizardContext: T, provider: string, resourceType: string): void; - - /** - * Used to convert a location into a home location and an extended location if the location passed in is an extended location. - * If the location passed in is not extended, then extendedLocation will be `undefined`. - * @param location location or extended location - */ - public static getExtendedLocation(location: AzExtLocation): { location: string, extendedLocation?: ExtendedLocation }; - - /** - * Gets the `autoSelectLocation` for this wizard. This location will be automatically selected during prompting, if available. - * @param wizardContext The context of the wizard - */ - public static getAutoSelectLocation(wizardContext: T): AzExtLocation | undefined; - - /** - * Gets the selected location for this wizard. - * @param wizardContext The context of the wizard - * @param provider If specified, this will check against that provider's supported locations and attempt to find a "related" location if the selected location is not supported. - * @param supportsExtendedLocations If set to true, the location returned may be an extended location, in which case the `extendedLocation` property should be added when creating a resource - */ - public static getLocation(wizardContext: T, provider?: string, supportsExtendedLocations?: boolean): Promise; - - /** - * Returns true if a location has been set on the context - */ - public static hasLocation(wizardContext: T): boolean; - - /** - * Used to get locations. By passing in the context, we can ensure that Azure is only queried once for the entire wizard - * @param wizardContext The context of the wizard. - */ - public static getLocations(wizardContext: T): Promise; - - /** - * Returns true if the given location matches the name - */ - public static locationMatchesName(location: AzExtLocation, name: string): boolean; - - public prompt(wizardContext: T): Promise; - public shouldPrompt(wizardContext: T): boolean; - - protected getQuickPicks(wizardContext: T): Promise[]>; - - public static generalizeLocationName(name: string | undefined): string; - - /** - * Implement this to set descriptions on location quick pick items. - */ - public static getQuickPickDescription?: (location: AzExtLocation) => string | undefined; -} - -/** - * Checks to see if providers (i.e. 'Microsoft.Web') are registered and registers them if they're not - */ -export declare class VerifyProvidersStep extends AzureWizardExecuteStep { - /** - * 90 - */ - public priority: number; - - /** - * @param providers List of providers to verify - */ - public constructor(providers: string[]); - - public execute(wizardContext: T, progress: Progress<{ message?: string; increment?: number }>): Promise; - public shouldExecute(wizardContext: T): boolean; -} - -export interface IResourceGroupWizardContext extends ILocationWizardContext, IRelatedNameWizardContext { - /** - * Internal value indicating the last resource group name checked with `ResourceGroupVerifyStep`. - * This name does not indicate a successful outcome from the verification step, it only indicates that the check has taken place. - */ - _lastResourceGroupNameVerified?: string; - - /** - * The resource group to use for new resources. - * If an existing resource group is picked, this value will be defined after `ResourceGroupListStep.prompt` occurs - * If a new resource group is picked, this value will be defined after the `execute` phase of the 'create' subwizard - */ - resourceGroup?: ResourceGroup; - - /** - * The task used to get existing resource groups. - * By specifying this in the context, we can ensure that Azure is only queried once for the entire wizard - */ - resourceGroupsTask?: Promise; - - newResourceGroupName?: string; - - /** - * By default, users will be prompted to select an existing resource group if creating one fails with a 403 error. Set this to `true` to prevent that behavior - */ - suppress403Handling?: boolean; - - /** - * Will automatically be generated by UserAssignedIdentityCreateStep if not specified by the user - */ - newManagedIdentityName?: string; - - /** - * The managed identity that will be assigned to the resource such as a function app or container app - * If you need to grant access to a resource, such as a storage account or SQL database, you can use this managed identity to create a role assignment - * with the RoleAssignmentExecuteStep - */ - managedIdentity?: Identity; - - ui: IAzureUserInput; -} - -export declare const resourceGroupNamingRules: IAzureNamingRules; - -export declare class ResourceGroupListStep extends AzureWizardPromptStep { - /** - * Used to get existing resource groups. By passing in the context, we can ensure that Azure is only queried once for the entire wizard - * @param wizardContext The context of the wizard. - */ - public static getResourceGroups(wizardContext: T): Promise; - - /** - * Checks existing resource groups in the wizard's subscription to see if the name is available. - * @param wizardContext The context of the wizard. - */ - public static isNameAvailable(wizardContext: T, name: string): Promise; - - public prompt(wizardContext: T): Promise; - public getSubWizard(wizardContext: T): Promise | undefined>; - public shouldPrompt(wizardContext: T): boolean; -} - -export declare class ResourceGroupNameStep extends AzureWizardPromptStep { - public prompt(wizardContext: T): Promise; - public shouldPrompt(wizardContext: T): boolean; -} - -export declare class ResourceGroupVerifyStep extends AzureWizardExecuteStepWithActivityOutput { - public stepName: string; - protected getOutputLogSuccess(context: T): string; - protected getOutputLogFail(context: T): string; - protected getTreeItemLabel(context: T): string; - - /** - * 95 - */ - public priority: number; - public configureBeforeExecute(wizardContext: T): void | Promise; - public execute(wizardContext: T, progress: Progress<{ message?: string; increment?: number }>): Promise; - public shouldExecute(wizardContext: T): boolean; -} - -export declare class ResourceGroupCreateStep extends AzureWizardExecuteStepWithActivityOutput { - public stepName: string; - protected getOutputLogSuccess(context: T): string; - protected getOutputLogFail(context: T): string; - protected getTreeItemLabel(context: T): string; - - /** - * 100 - */ - public priority: number; - public configureBeforeExecute(wizardContext: T): void | Promise; - public execute(wizardContext: T, progress: Progress<{ message?: string; increment?: number }>): Promise; - public shouldExecute(wizardContext: T): boolean; -} - -export interface IStorageAccountWizardContext extends IResourceGroupWizardContext { - /** - * The storage account to use. - * If an existing storage account is picked, this value will be defined after `StorageAccountListStep.prompt` occurs - * If a new storage account is picked, this value will be defined after the `execute` phase of the 'create' subwizard - */ - storageAccount?: StorageAccount; - - newStorageAccountName?: string; - /** - * This controls whether the storage account can generate connection strings. - * This should be disabled for storage accounts that are using managed identity only. - */ - disableSharedKeyAccess?: boolean; -} - -export declare enum StorageAccountKind { - Storage = "Storage", - StorageV2 = "StorageV2", - BlobStorage = "BlobStorage", - BlockBlobStorage = "BlockBlobStorage", -} - -export declare enum StorageAccountPerformance { - Standard = "Standard", - Premium = "Premium", -} - -export declare enum StorageAccountReplication { - /** - * Locally redundant storage - */ - LRS = "LRS", - /** - * Zone-redundant storage - */ - ZRS = "ZRS", - /** - * Geo-redundant storage - */ - GRS = "GRS", - /** - * Read-access geo-redundant storage - */ - RAGRS = "RAGRS", -} - -export interface INewStorageAccountDefaults { - kind: StorageAccountKind; - performance: StorageAccountPerformance; - replication: StorageAccountReplication; -} - -export interface IStorageAccountFilters { - kind?: StorageAccountKind[]; - performance?: StorageAccountPerformance[]; - replication?: StorageAccountReplication[]; - - /** - * If specified, a 'learn more' option will be displayed to explain why some storage accounts were filtered - */ - learnMoreLink?: string; -} - -export declare const storageAccountNamingRules: IAzureNamingRules; -export declare class StorageAccountListStep extends AzureWizardPromptStep { - /** - * @param createOptions Default options to use when creating a Storage Account - * @param filterOptions Optional filters used when listing Storage Accounts - */ - public constructor(createOptions: INewStorageAccountDefaults, filterOptions?: IStorageAccountFilters); - - public static isNameAvailable(wizardContext: T, name: string): Promise; - - public prompt(wizardContext: T): Promise; - public getSubWizard(wizardContext: T): Promise | undefined>; - public shouldPrompt(wizardContext: T): boolean; -} - -export declare class StorageAccountNameStep extends AzureNameStep { - public constructor(); - - public prompt(wizardContext: T): Promise; - public shouldPrompt(wizardContext: T): boolean; - - protected isRelatedNameAvailable(wizardContext: T, name: string): Promise; -} - -export declare class StorageAccountCreateStep extends AzureWizardExecuteStep { - /** - * 130 - */ - public priority: number; - public constructor(defaults: INewStorageAccountDefaults); - - public execute(wizardContext: T, progress: Progress<{ message?: string; increment?: number }>): Promise; - public shouldExecute(wizardContext: T): boolean; -} - -export declare class UserAssignedIdentityListStep extends AzureWizardPromptStep { - public constructor(suppressCreate?: boolean); - - public prompt(wizardContext: T): Promise; - public shouldPrompt(wizardContext: T): boolean; -} - -export declare class UserAssignedIdentityNameStep extends AzureWizardPromptStep { - public constructor(); - - public prompt(wizardContext: T): Promise; - public shouldPrompt(wizardContext: T): boolean; - - static isNameAvailable(wizardContext: IResourceGroupWizardContext, rgName: string, identityName: string): Promise; - static tryGenerateRelatedName(wizardContext: IResourceGroupWizardContext, rgName: string): Promise; -} - -export declare class UserAssignedIdentityCreateStep extends AzureWizardExecuteStep { - /** - * 140 - */ - public priority: number; - public constructor(); - - public execute(wizardContext: T, progress: Progress<{ message?: string; increment?: number }>): Promise; - public shouldExecute(wizardContext: T): boolean; -} - -export declare class RoleAssignmentExecuteStep extends AzureWizardExecuteStep { - /** - * 900 - */ - public priority: number; - /** - * @param getScopeId A function that returns the scope id for the role assignment. - * The scope ID is the Azure ID of the resource that we are granting access to such as a storage account. - * Example: `/subscriptions/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/rgName/providers/Microsoft.Storage/storageAccounts/resourceName` - * This typically won't exist until _after_ the wizard executes and the resource is created, so we need to pass in a function that returns the ID. - * If the scope ID is undefined, the step will throw an error. - * @param roles An array of roles. Each role is an object and include the ARM role definition id and name of the role definition. - * */ - public constructor(roles: () => (Role[] | Promise | undefined), options?: { priority?: number }); - - public execute(wizardContext: T, progress: Progress<{ message?: string; increment?: number }>): Promise; - public shouldExecute(wizardContext: T): boolean; -} - -export interface Role { - /** - * The scope of the operation or resource. Valid scopes are: subscription (format: - * '/subscriptions/{subscriptionId}'), resource group (format: - * '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}', or resource (format: - * '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/[{parentResourcePath}/]{resourceType}/{resourceName}' - */ - scopeId: string | undefined; - /** - * The role definition id of the role to assign. This can be created using `createRoleId` - */ - roleDefinitionId: string; - /** - * The name of the role definition to assign - */ - roleDefinitionName: string; -} - -export interface IAzureUtilsExtensionVariables extends UIExtensionVariables { - prefix: string; -} - -/** - * Call this to register common variables used throughout the UI package. - */ -export declare function registerAzureUtilsExtensionVariables(extVars: IAzureUtilsExtensionVariables): void; - -/** - * Credential type to be used for creating generic http rest clients - */ -export type AzExtGenericCredentials = AzExtServiceClientCredentialsT2 | AzExtServiceClientCredentials; -export type AzExtGenericClientInfo = AzExtGenericCredentials | { credentials: AzExtGenericCredentials; environment: Environment; } | undefined; - -/** - * Creates a generic http rest client (i.e. for non-Azure calls or for Azure calls that the available sdks don't support), ensuring best practices are followed. For example: - * 1. Adds extension-specific user agent - * 2. Uses resourceManagerEndpointUrl to support sovereigns (if clientInfo corresponds to an Azure environment) - * @param clientInfo The client/credentials info or `undefined` if no credentials are needed - */ -export declare function createGenericClient(context: IActionContext, clientInfo: AzExtGenericClientInfo | undefined, options?: IGenericClientOptions): Promise; -export interface IGenericClientOptions { - noRetryPolicy?: boolean; - addStatusCodePolicy?: boolean; - endpoint?: string; -} - -/** - * Used to create Azure clients for managed identity without having to install the sdk into client extension package.json - */ -export function createManagedServiceIdentityClient(context: AzExtClientContext): Promise -export function createAuthorizationManagementClient(context: AzExtClientContext): Promise - -export type AzExtRequestPrepareOptions = PipelineRequestOptions & { rejectUnauthorized?: boolean } -export type AzExtPipelineResponse = PipelineResponse & { parsedBody?: any } - -/** - * Send request with a timeout specified and turn off retry policy (because retrying could take a lot longer) - * @param timeout The timeout in milliseconds - * @param clientInfo The client/credentials info or `undefined` if no credentials are needed - */ -export declare function sendRequestWithTimeout(context: IActionContext, options: AzExtRequestPrepareOptions, timeout: number, clientInfo: AzExtGenericClientInfo): Promise; - -export type AzExtClientType = new (credentials: AzExtServiceClientCredentials, subscriptionId: string, options?: ServiceClientOptions) => T; - -/** - * Convenience type to give us multiple ways to specify subscription info and action context depending on the scenario - */ -export type AzExtClientContext = ISubscriptionActionContext | [IActionContext, ISubscriptionContext | AzExtTreeItem]; - -/** - * Converts `AzExtClientContext` into a single object: `ISubscriptionActionContext` - */ -export declare function parseClientContext(clientContext: AzExtClientContext): ISubscriptionActionContext; - -/** - * Creates an Azure client, ensuring best practices are followed. For example: - * 1. Adds extension-specific user agent - * 2. Uses resourceManagerEndpointUrl to support sovereigns - */ -export declare function createAzureClient(context: AzExtClientContext, clientType: AzExtClientType): T; - -export type AzExtSubscriptionClientType = new (credentials: AzExtServiceClientCredentials, options?: ServiceClientOptions) => T; - -/** - * Creates an Azure subscription client, ensuring best practices are followed. For example: - * 1. Adds extension-specific user agent - * 2. Uses resourceManagerEndpointUrl to support sovereigns - */ -export declare function createAzureSubscriptionClient(context: AzExtClientContext, clientType: AzExtSubscriptionClientType): T; - -export declare function createStorageClient(context: AzExtClientContext): Promise; - -export declare namespace uiUtils { - export function listAllIterator(iterator: PagedAsyncIterableIterator): Promise -} - -interface ParsedAzureResourceId { - rawId: string; - subscriptionId: string; - resourceGroup: string; - provider: string; - resourceName: string; -} - -interface ParsedAzureResourceGroupId { - rawId: string; - subscriptionId: string; - resourceGroup: string; -} - -export function parseAzureResourceId(id: string): ParsedAzureResourceId; -export function parseAzureResourceGroupId(id: string): ParsedAzureResourceGroupId; -export function getResourceGroupFromId(id: string): string; - -export declare function createPortalUri(subscription: AzureSubscription, id: string, options?: OpenInPortalOptions): Uri; - -/** - * Pipe Azure SDK logs into the provided log outptut channel using the @azure/logger package. - * - * @param logOutputChannel - log output channel to pipe logs into - */ -export function setupAzureLogger(logOutputChannel: LogOutputChannel): Disposable; - -/** - * Replaces the usage of BasicAuthenticationCredentials for ServiceClients imported from @azure/core-pipelines - * - * @param client - The service client. This will typically be a generalClient - * @param userName - Username to be used with basic authentication login - * @param password - Password. Gets encoded before being set in the header - */ -export function addBasicAuthenticationCredentialsToClient(client: ServiceClient, userName: string, password: string): void; - -/** - * Common Roles that should be used to assign permissions to resources - * The role definitions can be found here: https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles - */ -export declare const CommonRoleDefinitions: { - readonly storageBlobDataContributor: { - readonly name: "ba92f5b4-2d11-453d-a403-e96b0029c9fe"; - readonly type: "Microsoft.Authorization/roleDefinitions"; - readonly roleName: "Storage Blob Data Contributor"; - readonly description: "Allows for read, write and delete access to Azure Storage blob containers and data"; - readonly roleType: "BuiltInRole"; - }; - readonly storageBlobDataOwner: { - readonly name: "b7e6dc6d-f1e8-4753-8033-0f276bb0955b", - readonly type: "Microsoft.Authorization/roleDefinitions", - readonly roleName: "Storage Blob Data Owner", - readonly description: "Allows for full access to Azure Storage blob containers and data, including assigning POSIX access control.", - readonly roleType: "BuiltInRole" - }; - readonly storageQueueDataContributor: { - readonly name: "974c5e8b-45b9-4653-ba55-5f855dd0fb88", - readonly type: "Microsoft.Authorization/roleDefinitions", - readonly roleName: "Storage Queue Data Contributor", - readonly description: "Read, write, and delete Azure Storage queues and queue messages.", - readonly roleType: "BuiltInRole" - }; - readonly azureServiceBusDataReceiver: { - readonly name: "4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0", - readonly type: "Microsoft.Authorization/roleDefinitions", - readonly roleName: "Azure Service Bus Data Receiver", - readonly description: "Allows for receive access to Azure Service Bus resources.", - readonly sroleType: "BuiltInRole" - }; - readonly azureServiceBusDataOwner: { - readonly name: "090c5cfd-751d-490a-894a-3ce6f1109419", - readonly type: "Microsoft.Authorization/roleDefinitions", - readonly roleName: "Azure Service Bus Data Owner", - readonly description: "Allows for full access to Azure Service Bus resources.", - readonly roleType: "BuiltInRole" - }; - readonly azureEventHubsDataReceiver: { - readonly name: "a638d3c7-ab3a-418d-83e6-5f17a39d4fde", - readonly type: "Microsoft.Authorization/roleDefinitions", - readonly roleName: "Azure Event Hubs Data Receiver", - readonly description: "Allows receive access to Azure Event Hubs resources.", - readonly roleType: "BuiltInRole" - }; - readonly azureEventHubsDataOwner: { - readonly name: "f526a384-b230-433a-b45c-95f59c4a2dec", - readonly type: "Microsoft.Authorization/roleDefinitions", - readonly roleName: "Azure Event Hubs Data Owner", - readonly description: "Allows for full access to Azure Event Hubs resources.", - readonly roleType: "BuiltInRole" - }; - readonly cosmosDBAccountReader: { - readonly name: "fbdf93bf-df7d-467e-a4d2-9458aa1360c8", - readonly type: "Microsoft.Authorization/roleDefinitions", - readonly roleName: "Cosmos DB Account Reader", - readonly description: "Can read Azure Cosmos DB account data.", - readonly roleType: "BuiltInRole" - }; - readonly documentDBAccountContributor: { - readonly name: "5bd9cd88-fe45-4216-938b-f97437e15450", - readonly type: "Microsoft.Authorization/roleDefinitions", - readonly roleName: "DocumentDB Account Contributor", - readonly description: "Can manage Azure Cosmos DB accounts.", - readonly roleType: "BuiltInRole" - }, - readonly durableTaskDataContributor: { - name: "0ad04412-c4d5-4796-b79c-f76d14c8d402", - type: "Microsoft.Authorization/roleDefinitions", - roleName: "Durable Task Data Contributor", - description: "Durable Task role for all data access operations.", - roleType: "BuiltInRole" - }, -}; -/** - * Constructs the role id for a given subscription and role name id - * - * @param subscriptionId - Id for the subscription - * @param roleId - Name id for the role to be assigned (i.e CommonRoleDefinitions.storageBlobDataContributor.name) - */ -export function createRoleId(subscriptionId: string, RoleDefinition: RoleDefinition): string; - -/** - * creates all RoleDefinitionsItem for an entire managed identity object - */ -export function createRoleDefinitionsItems( - context: IActionContext, - subscription: AzureSubscription | ISubscriptionContext, - msi: Identity, - parentResourceId: string, -): Promise - -/** - * should not be created directly; use `createRoleDefinitionsItems` instead - */ -export type RoleDefinitionsItem = { - getChildren?(): ProviderResult; - getTreeItem(): TreeItem | Thenable; - id?: string | undefined; -} - -/** - * Requires a RoleDefinitionsItem as a data model in its constructor. Used for v1.5 API versions of the extensions - */ -export class RoleDefinitionsTreeItem extends AzExtParentTreeItem { - constructor(parent: AzExtParentTreeItem, roleDefinitionsItem: RoleDefinitionsItem); - public loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise; - public hasMoreChildrenImpl(): boolean; - public label: string; - public contextValue: string; -} - -export const IdentityProvider: string; -export const UserAssignedIdentityResourceType: string; diff --git a/azure/package-lock.json b/azure/package-lock.json index 05770ec620..64a1582d44 100644 --- a/azure/package-lock.json +++ b/azure/package-lock.json @@ -27,6 +27,7 @@ "devDependencies": { "@azure/core-auth": "^1.10.1", "@azure/core-paging": "^1.6.2", + "@microsoft/api-extractor": "^7.56.3", "@microsoft/vscode-azext-eng": "1.0.0-alpha.12", "@types/node": "22.x", "@types/semver": "^7.7.1", @@ -1023,6 +1024,29 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", + "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1105,6 +1129,100 @@ "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, + "node_modules/@microsoft/api-extractor": { + "version": "7.56.3", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.56.3.tgz", + "integrity": "sha512-fRqok4aRNq5GpgGBv2fKlSSKbirPKTJ75vQefthB5x9dwt4Zz+AezUzdc1p/AG4wUBIgmhjcEwn/Rj+N4Wh4Mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/api-extractor-model": "7.32.2", + "@microsoft/tsdoc": "~0.16.0", + "@microsoft/tsdoc-config": "~0.18.0", + "@rushstack/node-core-library": "5.19.1", + "@rushstack/rig-package": "0.6.0", + "@rushstack/terminal": "0.21.0", + "@rushstack/ts-command-line": "5.2.0", + "diff": "~8.0.2", + "lodash": "~4.17.23", + "minimatch": "10.1.2", + "resolve": "~1.22.1", + "semver": "~7.5.4", + "source-map": "~0.6.1", + "typescript": "5.8.2" + }, + "bin": { + "api-extractor": "bin/api-extractor" + } + }, + "node_modules/@microsoft/api-extractor-model": { + "version": "7.32.2", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.32.2.tgz", + "integrity": "sha512-Ussc25rAalc+4JJs9HNQE7TuO9y6jpYQX9nWD1DhqUzYPBr3Lr7O9intf+ZY8kD5HnIqeIRJX7ccCT0QyBy2Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "~0.16.0", + "@microsoft/tsdoc-config": "~0.18.0", + "@rushstack/node-core-library": "5.19.1" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/diff": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", + "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/minimatch": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", + "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/@microsoft/applicationinsights-channel-js": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.11.tgz", @@ -1188,6 +1306,50 @@ "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, + "node_modules/@microsoft/tsdoc": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz", + "integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.18.0.tgz", + "integrity": "sha512-8N/vClYyfOH+l4fLkkr9+myAoR6M7akc8ntBJ4DJdWH2b09uVfr71+LTMpNyG19fNqWDg8KEDZhx5wxuqHyGjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.16.0", + "ajv": "~8.12.0", + "jju": "~1.4.0", + "resolve": "~1.22.2" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/@microsoft/vscode-azext-eng": { "version": "1.0.0-alpha.12", "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-eng/-/vscode-azext-eng-1.0.0-alpha.12.tgz", @@ -1290,6 +1452,171 @@ "node": ">=14" } }, + "node_modules/@rushstack/node-core-library": { + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.19.1.tgz", + "integrity": "sha512-ESpb2Tajlatgbmzzukg6zyAhH+sICqJR2CNXNhXcEbz6UGCQfrKCtkxOpJTftWc8RGouroHG0Nud1SJAszvpmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "~8.13.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", + "fs-extra": "~11.3.0", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.5.4" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rushstack/node-core-library/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rushstack/problem-matcher": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@rushstack/problem-matcher/-/problem-matcher-0.1.1.tgz", + "integrity": "sha512-Fm5XtS7+G8HLcJHCWpES5VmeMyjAKaWeyZU5qPzZC+22mPlJzAsOxymHiWIfuirtPckX3aptWws+K2d0BzniJA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/rig-package": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.6.0.tgz", + "integrity": "sha512-ZQmfzsLE2+Y91GF15c65L/slMRVhF6Hycq04D4TwtdGaUAbIXXg9c5pKA5KFU7M4QMaihoobp9JJYpYcaY3zOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "~1.22.1", + "strip-json-comments": "~3.1.1" + } + }, + "node_modules/@rushstack/terminal": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.21.0.tgz", + "integrity": "sha512-cLaI4HwCNYmknM5ns4G+drqdEB6q3dCPV423+d3TZeBusYSSm09+nR7CnhzJMjJqeRcdMAaLnrA4M/3xDz4R3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "5.19.1", + "@rushstack/problem-matcher": "0.1.1", + "supports-color": "~8.1.1" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/terminal/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@rushstack/ts-command-line": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-5.2.0.tgz", + "integrity": "sha512-lYxCX0nDdkDtCkVpvF0m25ymf66SaMWuppbD6b7MdkIzvGXKBXNIVZlwBH/C0YfkanrupnICWf2n4z3AKSfaHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rushstack/terminal": "0.21.0", + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "string-argv": "~0.3.1" + } + }, + "node_modules/@rushstack/ts-command-line/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/@selderee/plugin-htmlparser2": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", @@ -1313,6 +1640,13 @@ "eslint": ">=7.7.0" } }, + "node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -1870,6 +2204,48 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -2675,6 +3051,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -2784,6 +3177,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/fs-extra": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz", + "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2799,6 +3207,16 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2925,6 +3343,19 @@ "node": ">=8" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -3037,6 +3468,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -3067,6 +3508,22 @@ "node": ">=8" } }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3225,6 +3682,13 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true, + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", @@ -3259,6 +3723,19 @@ "dev": true, "license": "MIT" }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -3331,6 +3808,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -3355,6 +3839,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -3752,6 +4249,13 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", @@ -3889,6 +4393,37 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -4024,6 +4559,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/stdin-discarder": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", @@ -4054,6 +4606,16 @@ "dev": true, "license": "MIT" }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -4184,6 +4746,19 @@ "node": ">=8" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tapable": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", @@ -4371,6 +4946,16 @@ "dev": true, "license": "MIT" }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -4552,6 +5137,13 @@ "node": ">=10" } }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/azure/package.json b/azure/package.json index bad7a0db3f..ad7598220d 100644 --- a/azure/package.json +++ b/azure/package.json @@ -13,7 +13,7 @@ ], "module": "dist/esm/src/index.js", "main": "dist/cjs/src/index.js", - "types": "index.d.ts", + "types": "dist/esm/src/index.d.ts", "license": "MIT", "repository": { "type": "git", diff --git a/azure/src/azure-account.api.d.ts b/azure/src/azure-account.api.ts similarity index 82% rename from azure/src/azure-account.api.d.ts rename to azure/src/azure-account.api.ts index a24627ee22..43f93857bf 100644 --- a/azure/src/azure-account.api.d.ts +++ b/azure/src/azure-account.api.ts @@ -9,8 +9,14 @@ import type { Environment } from '@azure/ms-rest-azure-env'; import type { ReadStream } from 'fs'; import type { CancellationToken, Event, Progress, Terminal } from 'vscode'; +/** + * @deprecated The Azure Account extension is deprecated. + */ export type AzureLoginStatus = 'Initializing' | 'LoggingIn' | 'LoggedIn' | 'LoggedOut'; +/** + * @deprecated The Azure Account extension is deprecated. + */ export interface AzureAccountExtensionApi { readonly apiVersion: string; readonly status: AzureLoginStatus; @@ -27,6 +33,9 @@ export interface AzureAccountExtensionApi { createCloudShell(os: 'Linux' | 'Windows'): CloudShell; } +/** + * @deprecated The Azure Account extension is deprecated. + */ export interface AzureSession { readonly environment: Environment; readonly userId: string; @@ -38,21 +47,36 @@ export interface AzureSession { readonly credentials2: TokenCredential; } +/** + * @deprecated The Azure Account extension is deprecated. + */ export interface AzureSubscription { readonly session: AzureSession; readonly subscription: Subscription; } +/** + * @deprecated The Azure Account extension is deprecated. + */ export type AzureResourceFilter = AzureSubscription; +/** + * @deprecated The Azure Account extension is deprecated. + */ export type CloudShellStatus = 'Connecting' | 'Connected' | 'Disconnected'; +/** + * @deprecated The Azure Account extension is deprecated. + */ export interface UploadOptions { contentLength?: number; progress?: Progress<{ message?: string; increment?: number }>; token?: CancellationToken; } +/** + * @deprecated The Azure Account extension is deprecated. + */ export interface CloudShell { readonly status: CloudShellStatus; readonly onStatusChanged: Event; diff --git a/azure/src/clients.ts b/azure/src/clients.ts index e5e3d36911..7ebed9097f 100644 --- a/azure/src/clients.ts +++ b/azure/src/clients.ts @@ -8,13 +8,14 @@ import type { ManagedServiceIdentityClient } from '@azure/arm-msi'; import type { ResourceManagementClient } from '@azure/arm-resources'; import type { SubscriptionClient } from '@azure/arm-resources-subscriptions'; import type { StorageManagementClient } from '@azure/arm-storage'; -import type { AzExtClientType } from '../index'; -import { createAzureClient, createAzureSubscriptionClient, InternalAzExtClientContext, parseClientContext } from './createAzureClient'; +import type { AzExtClientType } from './createAzureClient'; +import { createAzureClient, createAzureSubscriptionClient, parseClientContext } from './createAzureClient'; +import type { AzExtClientContext } from './createAzureClient'; // Lazy-load @azure packages to improve startup performance. // NOTE: The client is the only import that matters, the rest of the types disappear when compiled to JavaScript -export async function createStorageClient(context: InternalAzExtClientContext): Promise { +export async function createStorageClient(context: AzExtClientContext): Promise { if (parseClientContext(context).isCustomCloud) { return createAzureClient(context, (await import('@azure/arm-storage-profile-2020-09-01-hybrid')).StorageManagementClient); } else { @@ -22,7 +23,7 @@ export async function createStorageClient(context: InternalAzExtClientContext): } } -export async function createResourcesClient(context: InternalAzExtClientContext): Promise { +export async function createResourcesClient(context: AzExtClientContext): Promise { if (parseClientContext(context).isCustomCloud) { return createAzureClient(context, (await import('@azure/arm-resources-profile-2020-09-01-hybrid')).ResourceManagementClient); } else { @@ -30,11 +31,17 @@ export async function createResourcesClient(context: InternalAzExtClientContext) } } -export async function createManagedServiceIdentityClient(context: InternalAzExtClientContext): Promise { +/** + * Used to create Azure clients for managed identity without having to install the sdk into client extension package.json + */ +export async function createManagedServiceIdentityClient(context: AzExtClientContext): Promise { return createAzureClient(context, (await import('@azure/arm-msi')).ManagedServiceIdentityClient as unknown as AzExtClientType); } -export async function createAuthorizationManagementClient(context: InternalAzExtClientContext): Promise { +/** + * Used to create Azure clients for managed identity without having to install the sdk into client extension package.json + */ +export async function createAuthorizationManagementClient(context: AzExtClientContext): Promise { if (parseClientContext(context).isCustomCloud) { return createAzureClient(context, (await import('@azure/arm-authorization-profile-2020-09-01-hybrid')).AuthorizationManagementClient); } else { @@ -42,6 +49,6 @@ export async function createAuthorizationManagementClient(context: InternalAzExt } } -export async function createSubscriptionsClient(context: InternalAzExtClientContext): Promise { +export async function createSubscriptionsClient(context: AzExtClientContext): Promise { return createAzureSubscriptionClient(context, (await import('@azure/arm-resources-subscriptions')).SubscriptionClient); } diff --git a/azure/src/constants.ts b/azure/src/constants.ts index 697cbaf5d3..31cab0374d 100644 --- a/azure/src/constants.ts +++ b/azure/src/constants.ts @@ -12,6 +12,10 @@ export const storageProviderType = "Microsoft.Storage/storageAccounts"; export const IdentityProvider: string = 'Microsoft.ManagedIdentity'; export const UserAssignedIdentityResourceType: string = 'userAssignedIdentities'; +/** + * Common Roles that should be used to assign permissions to resources + * The role definitions can be found here: https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles + */ export const CommonRoleDefinitions = { storageBlobDataContributor: { name: "ba92f5b4-2d11-453d-a403-e96b0029c9fe", @@ -19,72 +23,78 @@ export const CommonRoleDefinitions = { roleName: "Storage Blob Data Contributor", description: "Allows for read, write and delete access to Azure Storage blob containers and data", roleType: "BuiltInRole" - } as RoleDefinition, + } as const, storageBlobDataOwner: { name: "b7e6dc6d-f1e8-4753-8033-0f276bb0955b", type: "Microsoft.Authorization/roleDefinitions", roleName: "Storage Blob Data Owner", description: "Allows for full access to Azure Storage blob containers and data, including assigning POSIX access control.", roleType: "BuiltInRole" - } as RoleDefinition, + } as const, storageQueueDataContributor: { name: "974c5e8b-45b9-4653-ba55-5f855dd0fb88", type: "Microsoft.Authorization/roleDefinitions", roleName: "Storage Queue Data Contributor", description: "Read, write, and delete Azure Storage queues and queue messages.", roleType: "BuiltInRole" - } as RoleDefinition, + } as const, azureServiceBusDataReceiver: { name: "4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0", type: "Microsoft.Authorization/roleDefinitions", roleName: "Azure Service Bus Data Receiver", description: "Allows for receive access to Azure Service Bus resources.", roleType: "BuiltInRole" - } as RoleDefinition, + } as const, azureServiceBusDataOwner: { name: "090c5cfd-751d-490a-894a-3ce6f1109419", type: "Microsoft.Authorization/roleDefinitions", roleName: "Azure Service Bus Data Owner", description: "Allows for full access to Azure Service Bus resources.", roleType: "BuiltInRole" - } as RoleDefinition, + } as const, azureEventHubsDataReceiver: { name: "a638d3c7-ab3a-418d-83e6-5f17a39d4fde", type: "Microsoft.Authorization/roleDefinitions", roleName: "Azure Event Hubs Data Receiver", description: "Allows receive access to Azure Event Hubs resources.", roleType: "BuiltInRole" - } as RoleDefinition, + } as const, azureEventHubsDataOwner: { name: "f526a384-b230-433a-b45c-95f59c4a2dec", type: "Microsoft.Authorization/roleDefinitions", roleName: "Azure Event Hubs Data Owner", description: "Allows for full access to Azure Event Hubs resources.", roleType: "BuiltInRole" - } as RoleDefinition, + } as const, cosmosDBAccountReader: { name: "fbdf93bf-df7d-467e-a4d2-9458aa1360c8", type: "Microsoft.Authorization/roleDefinitions", roleName: "Cosmos DB Account Reader", description: "Can read Azure Cosmos DB account data.", roleType: "BuiltInRole" - } as RoleDefinition, + } as const, documentDBAccountContributor: { name: "5bd9cd88-fe45-4216-938b-f97437e15450", type: "Microsoft.Authorization/roleDefinitions", roleName: "DocumentDB Account Contributor", description: "Can manage Azure Cosmos DB accounts.", roleType: "BuiltInRole" - } as RoleDefinition, + } as const, durableTaskDataContributor: { name: "0ad04412-c4d5-4796-b79c-f76d14c8d402", type: "Microsoft.Authorization/roleDefinitions", roleName: "Durable Task Data Contributor", description: "Durable Task role for all data access operations.", roleType: "BuiltInRole" - } as RoleDefinition, -} as const; + } as const, +} as const satisfies Record; +/** + * Constructs the role id for a given subscription and role name id + * + * @param subscriptionId - Id for the subscription + * @param roleId - Name id for the role to be assigned (i.e CommonRoleDefinitions.storageBlobDataContributor.name) + */ export function createRoleId(subscriptionId: string, roleDefinition: RoleDefinition): string { return `/subscriptions/${subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/${roleDefinition.name}`; } diff --git a/azure/src/createAzureClient.ts b/azure/src/createAzureClient.ts index 15546186ea..8128fe2f3d 100644 --- a/azure/src/createAzureClient.ts +++ b/azure/src/createAzureClient.ts @@ -3,18 +3,45 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ServiceClient } from '@azure/core-client'; -import { createHttpHeaders, createPipelineRequest, defaultRetryPolicy, Pipeline, PipelineOptions, PipelinePolicy, PipelineRequest, PipelineResponse, RestError, RetryPolicyOptions, SendRequest, userAgentPolicy } from '@azure/core-rest-pipeline'; -import { appendExtensionUserAgent, AzExtServiceClientCredentialsT2, AzExtTreeItem, IActionContext, ISubscriptionActionContext, ISubscriptionContext, parseError } from '@microsoft/vscode-azext-utils'; +import { ServiceClient, ServiceClientOptions } from '@azure/core-client'; +import { createHttpHeaders, createPipelineRequest, defaultRetryPolicy, Pipeline, PipelineOptions, PipelinePolicy, PipelineRequest, PipelineRequestOptions, PipelineResponse, RestError, RetryPolicyOptions, SendRequest, userAgentPolicy } from '@azure/core-rest-pipeline'; +import type { Environment } from '@azure/ms-rest-azure-env'; +import { appendExtensionUserAgent, AzExtServiceClientCredentials, AzExtServiceClientCredentialsT2, AzExtTreeItem, IActionContext, ISubscriptionActionContext, ISubscriptionContext, parseError } from '@microsoft/vscode-azext-utils'; import { randomUUID } from 'crypto'; import { Agent as HttpsAgent } from 'https'; import * as vscode from "vscode"; -import * as types from '../index'; import { parseJson, removeBom } from './utils/parseJson'; -export type InternalAzExtClientContext = ISubscriptionActionContext | [IActionContext, ISubscriptionContext | AzExtTreeItem]; +export type AzExtClientType = new (credentials: AzExtServiceClientCredentials, subscriptionId: string, options?: ServiceClientOptions) => T; -export function parseClientContext(clientContext: InternalAzExtClientContext): ISubscriptionActionContext { +/** + * Convenience type to give us multiple ways to specify subscription info and action context depending on the scenario + */ +export type AzExtClientContext = ISubscriptionActionContext | [IActionContext, ISubscriptionContext | AzExtTreeItem]; + +/** + * Credential type to be used for creating generic http rest clients + */ +// eslint-disable-next-line @typescript-eslint/no-duplicate-type-constituents +export type AzExtGenericCredentials = AzExtServiceClientCredentials | AzExtServiceClientCredentialsT2; +export type AzExtGenericClientInfo = AzExtGenericCredentials | { credentials: AzExtGenericCredentials; environment: Environment; } | undefined; + +export interface IGenericClientOptions { + noRetryPolicy?: boolean; + addStatusCodePolicy?: boolean; + endpoint?: string; +} + +export type AzExtRequestPrepareOptions = PipelineRequestOptions & { rejectUnauthorized?: boolean } +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type AzExtPipelineResponse = PipelineResponse & { parsedBody?: any } + +export type AzExtSubscriptionClientType = new (credentials: AzExtServiceClientCredentials, options?: ServiceClientOptions) => T; + +/** + * Converts `AzExtClientContext` into a single object: `ISubscriptionActionContext` + */ +export function parseClientContext(clientContext: AzExtClientContext): ISubscriptionActionContext { if (Array.isArray(clientContext)) { const subscription = clientContext[1] instanceof AzExtTreeItem ? clientContext[1].subscription : clientContext[1]; // Make sure to copy over just the subscription info and not any other extraneous properties @@ -44,7 +71,12 @@ function getChallengeHandlerFromCredential(createCredentialsForScopes: (request: return getTokenForChallenge; } -export function createAzureClient(clientContext: InternalAzExtClientContext, clientType: types.AzExtClientType): T { +/** + * Creates an Azure client, ensuring best practices are followed. For example: + * 1. Adds extension-specific user agent + * 2. Uses resourceManagerEndpointUrl to support sovereigns + */ +export function createAzureClient(clientContext: AzExtClientContext, clientType: AzExtClientType): T { const context = parseClientContext(clientContext); const client = new clientType(context.credentials, context.subscriptionId, { endpoint: context.environment.resourceManagerEndpointUrl, @@ -66,7 +98,12 @@ export function createAzureClient(clientContext: Intern return client; } -export function createAzureSubscriptionClient(clientContext: InternalAzExtClientContext, clientType: types.AzExtSubscriptionClientType): T { +/** + * Creates an Azure subscription client, ensuring best practices are followed. For example: + * 1. Adds extension-specific user agent + * 2. Uses resourceManagerEndpointUrl to support sovereigns + */ +export function createAzureSubscriptionClient(clientContext: AzExtClientContext, clientType: AzExtSubscriptionClientType): T { const context = parseClientContext(clientContext); const client = new clientType(context.credentials, { endpoint: context.environment.resourceManagerEndpointUrl @@ -88,7 +125,12 @@ export function createAzureSubscriptionClient(clientCon return client; } -export async function sendRequestWithTimeout(context: IActionContext, options: types.AzExtRequestPrepareOptions, timeout: number, clientInfo: types.AzExtGenericClientInfo): Promise { +/** + * Send request with a timeout specified and turn off retry policy (because retrying could take a lot longer) + * @param timeout The timeout in milliseconds + * @param clientInfo The client/credentials info or `undefined` if no credentials are needed + */ +export async function sendRequestWithTimeout(context: IActionContext, options: AzExtRequestPrepareOptions, timeout: number, clientInfo: AzExtGenericClientInfo): Promise { const request: PipelineRequest = createPipelineRequest({ ...options, timeout @@ -103,8 +145,14 @@ export async function sendRequestWithTimeout(context: IActionContext, options: t } -export async function createGenericClient(context: IActionContext, clientInfo: types.AzExtGenericClientInfo | undefined, options?: types.IGenericClientOptions): Promise { - let credentials: types.AzExtGenericCredentials | undefined; +/** + * Creates a generic http rest client (i.e. for non-Azure calls or for Azure calls that the available sdks don't support), ensuring best practices are followed. For example: + * 1. Adds extension-specific user agent + * 2. Uses resourceManagerEndpointUrl to support sovereigns (if clientInfo corresponds to an Azure environment) + * @param clientInfo The client/credentials info or `undefined` if no credentials are needed + */ +export async function createGenericClient(context: IActionContext, clientInfo: AzExtGenericClientInfo | undefined, options?: IGenericClientOptions): Promise { + let credentials: AzExtGenericCredentials | undefined; let endpoint: string | undefined; if (clientInfo && 'credentials' in clientInfo) { credentials = clientInfo.credentials; @@ -168,6 +216,13 @@ function addAzExtPipeline(context: IActionContext, pipeline: Pipeline, endpoint? return pipeline; } +/** + * Replaces the usage of BasicAuthenticationCredentials for ServiceClients imported from @azure/core-pipelines + * + * @param client - The service client. This will typically be a generalClient + * @param userName - Username to be used with basic authentication login + * @param password - Password. Gets encoded before being set in the header + */ export function addBasicAuthenticationCredentialsToClient(client: ServiceClient, userName: string, password: string): void { client.pipeline.addPolicy(new BasicAuthenticationCredentialsPolicy(userName, password), { phase: 'Serialize' }); } @@ -175,7 +230,7 @@ export function addBasicAuthenticationCredentialsToClient(client: ServiceClient, /** * Automatically add id to correlate our telemetry with the platform team's telemetry */ -export class CorrelationIdPolicy implements PipelinePolicy { +class CorrelationIdPolicy implements PipelinePolicy { public readonly name = 'CorrelationIdPolicy'; constructor(private readonly context: IActionContext) { @@ -266,8 +321,8 @@ class AddEndpointPolicy implements PipelinePolicy { class StatusCodePolicy implements PipelinePolicy { public readonly name = 'StatusCodePolicy'; - public async sendRequest(request: PipelineRequest, next: SendRequest): Promise { - const response: types.AzExtPipelineResponse = await next(request); + public async sendRequest(request: PipelineRequest, next: SendRequest): Promise { + const response: AzExtPipelineResponse = await next(request); if (response.status < 200 || response.status >= 300) { const errorMessage: string = response.bodyAsText ? parseError(response.parsedBody || response.bodyAsText).message : diff --git a/azure/src/extensionVariables.ts b/azure/src/extensionVariables.ts index 8910d173bd..89d009d48b 100644 --- a/azure/src/extensionVariables.ts +++ b/azure/src/extensionVariables.ts @@ -6,6 +6,10 @@ import { IAzExtOutputChannel, registerUIExtensionVariables, UIExtensionVariables } from '@microsoft/vscode-azext-utils'; import { ExtensionContext, l10n } from "vscode"; +export interface IAzureUtilsExtensionVariables extends UIExtensionVariables { + prefix: string; +} + class UninitializedExtensionVariables implements UIExtensionVariables { private _error: Error = new Error(l10n.t('"registerAzureUtilsExtensionVariables" must be called before using the @microsoft/vscode-azext-azureutils package.')); @@ -24,9 +28,9 @@ class UninitializedExtensionVariables implements UIExtensionVariables { export let ext: UIExtensionVariables = new UninitializedExtensionVariables(); /** - * Call this to register common variables used throughout the AppService package. + * Call this to register common variables used throughout the azureutils package. */ -export function registerAzureUtilsExtensionVariables(extVars: UIExtensionVariables): void { +export function registerAzureUtilsExtensionVariables(extVars: IAzureUtilsExtensionVariables): void { if (ext === extVars) { // already registered return; diff --git a/azure/src/index.ts b/azure/src/index.ts index 575db399c0..df06089617 100644 --- a/azure/src/index.ts +++ b/azure/src/index.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +export type * from './azure-account.api'; export * from './clients'; export { CommonRoleDefinitions, createRoleId, IdentityProvider, UserAssignedIdentityResourceType } from './constants'; export * from './createAzureClient'; -export { registerAzureUtilsExtensionVariables } from './extensionVariables'; +export { IAzureUtilsExtensionVariables, registerAzureUtilsExtensionVariables } from './extensionVariables'; export * from './openInPortal'; export * from './tree/AzureAccountTreeItemBase'; export * from './tree/RoleDefinitionsItem'; @@ -26,5 +27,8 @@ export * from './wizard/StorageAccountListStep'; export * from './wizard/StorageAccountNameStep'; export * from './wizard/UserAssignedIdentityCreateStep'; export * from './wizard/UserAssignedIdentityListStep'; +export * from './wizard/UserAssignedIdentityNameStep'; export * from './wizard/VerifyProvidersStep'; +export * from './wizard/resourceGroupWizardTypes'; +export * from './wizard/storageWizardTypes'; // 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 diff --git a/azure/src/openInPortal.ts b/azure/src/openInPortal.ts index 132f403640..fb95805819 100644 --- a/azure/src/openInPortal.ts +++ b/azure/src/openInPortal.ts @@ -3,10 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as types from '../index'; import { AzExtTreeItem, isAzExtTreeItem, ISubscriptionContext, openUrl } from '@microsoft/vscode-azext-utils'; -export async function openInPortal(root: ISubscriptionContext | AzExtTreeItem, id: string, options?: types.OpenInPortalOptions): Promise { +export type OpenInPortalOptions = { + /** + * A query string applied directly to the host URL, e.g. "feature.staticwebsites=true" (turns on a preview feature) + */ + queryPrefix?: string; +}; + +/** + * Combines the root.environment.portalLink and id to open the resource in the portal. + * + * NOTE: If root is a tree item, it will find the subscription ancestor and get environment.portalLink from there + */ +export async function openInPortal(root: ISubscriptionContext | AzExtTreeItem, id: string, options?: OpenInPortalOptions): Promise { root = isAzExtTreeItem(root) ? root.subscription : root; const queryPrefix: string = (options?.queryPrefix) ? `?${options.queryPrefix}` : ''; diff --git a/azure/src/tree/AzureAccountTreeItemBase.ts b/azure/src/tree/AzureAccountTreeItemBase.ts index 9200826700..107b57516d 100644 --- a/azure/src/tree/AzureAccountTreeItemBase.ts +++ b/azure/src/tree/AzureAccountTreeItemBase.ts @@ -6,7 +6,6 @@ import { addExtensionValueToMask, AzExtParentTreeItem, AzExtServiceClientCredentials, AzExtTreeItem, AzureWizardPromptStep, GenericTreeItem, IActionContext, ISubscriptionActionContext, ISubscriptionContext, nonNullProp, nonNullValue, registerEvent, TreeItemIconPath, UserCancelledError } from '@microsoft/vscode-azext-utils'; import * as semver from 'semver'; import { commands, Disposable, Extension, extensions, l10n, MessageItem, ProgressLocation, ThemeIcon, window } from 'vscode'; -import * as types from '../../index'; import { AzureAccountExtensionApi, AzureLoginStatus, AzureResourceFilter } from '../azure-account.api'; import { getIconPath } from './IconPath'; import { SubscriptionTreeItemBase } from './SubscriptionTreeItemBase'; @@ -25,8 +24,12 @@ const extensionOpenCommand: string = 'extension.open'; type AzureAccountResult = AzureAccountExtensionApi | 'notInstalled' | 'needsUpdate'; const minAccountExtensionVersion: string = '0.9.0'; -export abstract class AzureAccountTreeItemBase extends AzExtParentTreeItem implements types.AzureAccountTreeItemBase { - public static contextValue: string = 'azureextensionui.azureAccount'; +/** + * A tree item for an Azure Account, which will display subscriptions. For Azure-centered extensions, this will be at the root of the tree. + * @deprecated The Azure Account extension is deprecated. + */ +export abstract class AzureAccountTreeItemBase extends AzExtParentTreeItem implements Disposable { + public static readonly contextValue: string = 'azureextensionui.azureAccount'; public readonly contextValue: string = AzureAccountTreeItemBase.contextValue; public readonly label: string = 'Azure'; public childTypeLabel: string = l10n.t('subscription'); @@ -38,6 +41,11 @@ export abstract class AzureAccountTreeItemBase extends AzExtParentTreeItem imple private _subscriptionTreeItems: SubscriptionTreeItemBase[] | undefined; private _testAccount: AzureAccountExtensionApi | undefined; + /** + * Azure Account Tree Item + * @param parent The parent of this node or undefined if it's the root of the tree. + * @param testAccount Unofficial api for testing + */ constructor(parent?: AzExtParentTreeItem, testAccount?: AzureAccountExtensionApi) { super(parent); this._testAccount = testAccount; @@ -45,6 +53,10 @@ export abstract class AzureAccountTreeItemBase extends AzExtParentTreeItem imple } //#region Methods implemented by base class + /** + * Implement this to create a subscription tree item under this Azure Account node + * @param root Contains basic information about the subscription - should be passed in to the constructor of `SubscriptionTreeItemBase` + */ public abstract createSubscriptionTreeItem(root: ISubscriptionContext): SubscriptionTreeItemBase | Promise; //#endregion @@ -164,6 +176,10 @@ export abstract class AzureAccountTreeItemBase extends AzExtParentTreeItem imple return typeof azureAccount !== 'string' && azureAccount.status === 'LoggedIn'; } + /** + * If user is logged in and only has one subscription selected, adds that to the wizardContext and returns undefined + * Else, returns a prompt step for a subscription + */ public async getSubscriptionPromptStep(context: Partial & IActionContext): Promise | undefined> { const subscriptionNodes: SubscriptionTreeItemBase[] = await this.ensureSubscriptionTreeItems(context); if (subscriptionNodes.length === 1) { diff --git a/azure/src/tree/RoleDefinitionsItem.ts b/azure/src/tree/RoleDefinitionsItem.ts index 199cde3868..371a4d264f 100644 --- a/azure/src/tree/RoleDefinitionsItem.ts +++ b/azure/src/tree/RoleDefinitionsItem.ts @@ -8,13 +8,16 @@ import type { Identity } from "@azure/arm-msi"; import { AzExtParentTreeItem, AzExtTreeItem, createGenericElement, createSubscriptionContext, GenericTreeItem, IActionContext, ISubscriptionContext, TreeElementBase, TreeItemIconPath } from "@microsoft/vscode-azext-utils"; import { AzExtResourceType, AzureSubscription, getAzExtResourceType } from "@microsoft/vscode-azureresources-api"; import { ThemeIcon, TreeItem, TreeItemCollapsibleState, Uri } from "vscode"; -import * as types from '../../index'; +import { ParsedAzureResourceGroupId, ParsedAzureResourceId } from '../utils/parseAzureResourceId'; import { createAuthorizationManagementClient, createSubscriptionsClient } from "../clients"; import { createPortalUri } from "../utils/createPortalUri"; import { parseAzureResourceGroupId, parseAzureResourceId } from "../utils/parseAzureResourceId"; import { uiUtils } from "../utils/uiUtils"; import { getAzureIconPath } from "./IconPath"; +/** + * creates all RoleDefinitionsItem for an entire managed identity object + */ export async function createRoleDefinitionsItems(context: IActionContext, subscription: AzureSubscription | ISubscriptionContext, msi: Identity, parentResourceId: string): Promise { const subContext = isAzureSubscription(subscription) ? createSubscriptionContext(subscription) : subscription; const authClient = await createAuthorizationManagementClient([context, subContext]); @@ -54,6 +57,9 @@ export async function createRoleDefinitionsItems(context: IActionContext, subscr return roleDefinitionsItems; } +/** + * should not be created directly; use `createRoleDefinitionsItems` instead + */ export class RoleDefinitionsItem implements TreeElementBase { public id: string; public label: string; @@ -103,8 +109,8 @@ export class RoleDefinitionsItem implements TreeElementBase { withDescription?: boolean }): Promise { - let parsedScopeId: types.ParsedAzureResourceId | undefined; - let parsedAzureResourceGroupId: types.ParsedAzureResourceGroupId | undefined; + let parsedScopeId: ParsedAzureResourceId | undefined; + let parsedAzureResourceGroupId: ParsedAzureResourceGroupId | undefined; let label: string; let iconPath: TreeItemIconPath; let subscriptionId: string | undefined; @@ -152,7 +158,7 @@ export class RoleDefinitionsItem implements TreeElementBase { }); } - private static getRoleDefinitionsResourceLabel(parsedScopeId: types.ParsedAzureResourceId): string { + private static getRoleDefinitionsResourceLabel(parsedScopeId: ParsedAzureResourceId): string { const scopeId: string = parsedScopeId.rawId; if (parsedScopeId.provider.startsWith('Microsoft.DurableTask')) { @@ -198,13 +204,15 @@ export class RoleDefinitionsItem implements TreeElementBase { } // v1.5 implementation of RoleDefinitionsItem that uses RoleDefinitionsItem in the constructor +/** + * Requires a RoleDefinitionsItem as a data model in its constructor. Used for v1.5 API versions of the extensions + */ export class RoleDefinitionsTreeItem extends AzExtParentTreeItem { public label: string; public static contextValue: string = 'azureRoleDefinitions'; public readonly contextValue: string = RoleDefinitionsTreeItem.contextValue; - - constructor(parent: AzExtParentTreeItem, readonly roleDefinitionsItem: RoleDefinitionsItem) { + constructor(parent: AzExtParentTreeItem, private readonly roleDefinitionsItem: RoleDefinitionsItem) { super(parent); this.id = roleDefinitionsItem.id; this.label = roleDefinitionsItem.label; diff --git a/azure/src/tree/SubscriptionTreeItemBase.ts b/azure/src/tree/SubscriptionTreeItemBase.ts index 6f89fbff08..9d79be185c 100644 --- a/azure/src/tree/SubscriptionTreeItemBase.ts +++ b/azure/src/tree/SubscriptionTreeItemBase.ts @@ -3,11 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as types from '../../index'; import { AzExtParentTreeItem, ISubscriptionContext } from '@microsoft/vscode-azext-utils'; import { getIconPath } from './IconPath'; -export abstract class SubscriptionTreeItemBase extends AzExtParentTreeItem implements types.SubscriptionTreeItemBase { +/** + * Implement this class to display resources under a standard subscription tree item + */ +export abstract class SubscriptionTreeItemBase extends AzExtParentTreeItem { public static readonly contextValue: string = 'azureextensionui.azureSubscription'; public readonly contextValue: string = SubscriptionTreeItemBase.contextValue; public readonly label: string; diff --git a/azure/src/utils/createPortalUri.ts b/azure/src/utils/createPortalUri.ts index 3fef9e42cb..e9441d67f7 100644 --- a/azure/src/utils/createPortalUri.ts +++ b/azure/src/utils/createPortalUri.ts @@ -6,7 +6,7 @@ import { ISubscriptionContext } from "@microsoft/vscode-azext-utils"; import type { AzureSubscription } from "@microsoft/vscode-azureresources-api"; import * as vscode from 'vscode'; -import { OpenInPortalOptions } from "../../index"; +import { OpenInPortalOptions } from "../openInPortal"; export function createPortalUri(subscription: AzureSubscription | ISubscriptionContext, id: string, options?: OpenInPortalOptions): vscode.Uri { const queryPrefix: string = (options?.queryPrefix) ? `?${options.queryPrefix}` : ''; diff --git a/azure/src/utils/parseAzureResourceId.ts b/azure/src/utils/parseAzureResourceId.ts index e642620eab..5d71230684 100644 --- a/azure/src/utils/parseAzureResourceId.ts +++ b/azure/src/utils/parseAzureResourceId.ts @@ -4,9 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as types from '../../index'; -export function parseAzureResourceId(id: string): types.ParsedAzureResourceId { +export interface ParsedAzureResourceId { + rawId: string; + subscriptionId: string; + resourceGroup: string; + provider: string; + resourceName: string; +} + +export interface ParsedAzureResourceGroupId { + rawId: string; + subscriptionId: string; + resourceGroup: string; +} + +export function parseAzureResourceId(id: string): ParsedAzureResourceId { // eslint-disable-next-line @typescript-eslint/prefer-regexp-exec const matches: RegExpMatchArray | null = id.match(/\/subscriptions\/(.*)\/resourceGroups\/(.*)\/providers\/(.*)\/(.*)/i); @@ -27,7 +40,7 @@ export function parseAzureResourceId(id: string): types.ParsedAzureResourceId { * Parses the `subscriptionId` and `resourceGroup` off of an Azure Resource Group Id * (also compatible with generic Azure Resource Ids) */ -export function parseAzureResourceGroupId(id: string): types.ParsedAzureResourceGroupId { +export function parseAzureResourceGroupId(id: string): ParsedAzureResourceGroupId { // eslint-disable-next-line @typescript-eslint/prefer-regexp-exec, no-useless-escape const matches: RegExpMatchArray | null = id.match(/\/subscriptions\/(.*)\/resourceGroups\/([^\/]*)/i); diff --git a/azure/src/utils/setupAzureLogger.ts b/azure/src/utils/setupAzureLogger.ts index aec47093ee..e03067d956 100644 --- a/azure/src/utils/setupAzureLogger.ts +++ b/azure/src/utils/setupAzureLogger.ts @@ -6,6 +6,11 @@ import { AzureLogger, AzureLogLevel, setLogLevel } from "@azure/logger"; import * as vscode from "vscode"; +/** + * Pipe Azure SDK logs into the provided log output channel using the @azure/logger package. + * + * @param logOutputChannel - log output channel to pipe logs into + */ export function setupAzureLogger(logOutputChannel: vscode.LogOutputChannel): vscode.Disposable { const logLevelMap: Record = { // passing undefined to AzureLogger.setLogLevel disables logging diff --git a/azure/src/wizard/LocationListStep.ts b/azure/src/wizard/LocationListStep.ts index fc64866893..8a8dd4d668 100644 --- a/azure/src/wizard/LocationListStep.ts +++ b/azure/src/wizard/LocationListStep.ts @@ -7,19 +7,25 @@ import type { ExtendedLocation, Provider } from '@azure/arm-resources'; import type { Location } from '@azure/arm-resources-subscriptions'; import { AgentQuickPickItem, AgentQuickPickOptions, AzureWizardPromptStep, IActionContext, IAzureAgentInput, IAzureQuickPickItem, IAzureQuickPickOptions, nonNullProp, nonNullValue } from '@microsoft/vscode-azext-utils'; import * as vscode from 'vscode'; -import * as types from '../../index'; +import { ILocationWizardContext } from './resourceGroupWizardTypes'; import { createResourcesClient, createSubscriptionsClient } from '../clients'; import { resourcesProvider } from '../constants'; import { ext } from '../extensionVariables'; import { uiUtils } from '../utils/uiUtils'; +export type AzExtLocation = Location & { + id: string; + name: string; + displayName: string; +} + /* eslint-disable @typescript-eslint/naming-convention */ -interface ILocationWizardContextInternal extends types.ILocationWizardContext { +interface ILocationWizardContextInternal extends ILocationWizardContext { /** * The task used to get locations. * By specifying this in the context, we can ensure that Azure is only queried once for the entire wizard */ - _allLocationsTask?: Promise; + _allLocationsTask?: Promise; _alreadyHasLocationStep?: boolean; @@ -31,14 +37,14 @@ interface ILocationWizardContextInternal extends types.ILocationWizardContext { /** * The selected location. There's a small chance it's not supported by all providers if `setLocation` was used */ - _location?: types.AzExtLocation; + _location?: AzExtLocation; /** * The location to auto-select during prompting, if available. * Leverage this rather than `setLocation` when you want to automatically select a location * that respects all future resource providers. */ - _autoSelectLocation?: types.AzExtLocation; + _autoSelectLocation?: AzExtLocation; /** * Location list step is intended to be compatible with an {@link IAzureAgentInput}, so we re-type `ui`. @@ -47,63 +53,115 @@ interface ILocationWizardContextInternal extends types.ILocationWizardContext { } /* eslint-enable @typescript-eslint/naming-convention */ -export class LocationListStep extends AzureWizardPromptStep { +export class LocationListStep extends AzureWizardPromptStep { protected constructor(private options?: IAzureQuickPickOptions) { super(); } - public static addStep(wizardContext: IActionContext & Partial, promptSteps: AzureWizardPromptStep[], options?: IAzureQuickPickOptions): void { - if (!wizardContext._alreadyHasLocationStep) { + /** + * Adds a LocationListStep to the wizard. This function will ensure there is only one LocationListStep per wizard context. + * @param wizardContext The context of the wizard + * @param promptSteps The array of steps to include the LocationListStep to + * @param options Options to pass to ui.showQuickPick. Options are spread onto the defaults. + */ + public static addStep(wizardContext: IActionContext & Partial, promptSteps: AzureWizardPromptStep[], options?: IAzureQuickPickOptions): void { + const ctx = wizardContext as IActionContext & Partial; + if (!ctx._alreadyHasLocationStep) { promptSteps.push(new this(options)); - wizardContext._alreadyHasLocationStep = true; + ctx._alreadyHasLocationStep = true; } } - private static getInternalVariables(wizardContext: T): [Promise, Map>] { - wizardContext._allLocationsTask ??= getAllLocations(wizardContext); + private static getInternalVariables(wizardContext: ILocationWizardContext): [Promise, Map>] { + const ctx = wizardContext as ILocationWizardContextInternal; + ctx._allLocationsTask ??= getAllLocations(wizardContext); - if (!wizardContext._providerLocationsMap) { - wizardContext._providerLocationsMap = new Map>(); + if (!ctx._providerLocationsMap) { + ctx._providerLocationsMap = new Map>(); // Should be relevant for all our wizards this.addProviderForFiltering(wizardContext, resourcesProvider, 'resourceGroups'); } - return [wizardContext._allLocationsTask, wizardContext._providerLocationsMap]; + return [ctx._allLocationsTask, ctx._providerLocationsMap]; } - public static async setLocation(wizardContext: T, name: string): Promise { + /** + * This will set the wizard context's location (in which case the user will _not_ be prompted for location) + * For example, if the user selects an existing resource, you might want to use that location as the default for the wizard's other resources + * This _will_ set the location even if not all providers support it - in the hopes that a related location can be found during `getLocation` + * @param wizardContext The context of the wizard + * @param name The name or display name of the location + */ + public static async setLocation(wizardContext: T, name: string): Promise { + const ctx = wizardContext as T & ILocationWizardContextInternal; const [allLocationsTask] = this.getInternalVariables(wizardContext); - wizardContext._location = (await allLocationsTask).find(l => LocationListStep.locationMatchesName(l, name)); - wizardContext.telemetry.properties.locationType = wizardContext._location?.type; + ctx._location = (await allLocationsTask).find(l => LocationListStep.locationMatchesName(l, name)); + wizardContext.telemetry.properties.locationType = ctx._location?.type; } - public static setLocationSubset(wizardContext: T, task: Promise, provider: string): void { + /** + * Specify a task that will be used to filter locations + * @param wizardContext The context of the wizard + * @param task A task evaluating to the locations supported by this provider + * @param provider The relevant provider (i.e. 'Microsoft.Web') + */ + public static setLocationSubset(wizardContext: T, task: Promise, provider: string): void { const [, providerLocationsMap] = this.getInternalVariables(wizardContext); providerLocationsMap.set(provider.toLowerCase(), task); } - public static async setAutoSelectLocation(wizardContext: T, name: string): Promise { + /** + * Sets a location to auto-select during prompting, if available. + * Use this instead of `setLocation` when you want to automatically select a location + * that respects all future resource providers. + * @param wizardContext The context of the wizard + * @param name The name or display name of the suggested location + */ + public static async setAutoSelectLocation(wizardContext: T, name: string): Promise { + const ctx = wizardContext as T & ILocationWizardContextInternal; const [allLocationsTask] = this.getInternalVariables(wizardContext); - wizardContext._autoSelectLocation = (await allLocationsTask).find(l => LocationListStep.locationMatchesName(l, name)); - wizardContext.telemetry.properties.autoSelectLocationType = wizardContext._autoSelectLocation?.type; + ctx._autoSelectLocation = (await allLocationsTask).find(l => LocationListStep.locationMatchesName(l, name)); + wizardContext.telemetry.properties.autoSelectLocationType = ctx._autoSelectLocation?.type; } - public static resetLocation(wizardContext: T): void { - wizardContext._location = undefined; - wizardContext._allLocationsTask = undefined; - wizardContext._providerLocationsMap = undefined; - wizardContext._alreadyHasLocationStep = undefined; - wizardContext._autoSelectLocation = undefined; + /** + * Resets all location and location-related metadata on the wizard context back to its uninitialized state. + * This includes clearing the selected location, cached location tasks, provider location maps, and any step-tracking flags. + * Use this to ensure the wizard context is fully reset before starting a new location selection process. + * @param wizardContext The context of the wizard + */ + public static resetLocation(wizardContext: T): void { + const ctx = wizardContext as T & ILocationWizardContextInternal; + ctx._location = undefined; + ctx._allLocationsTask = undefined; + ctx._providerLocationsMap = undefined; + ctx._alreadyHasLocationStep = undefined; + ctx._autoSelectLocation = undefined; } - public static addProviderForFiltering(wizardContext: T, provider: string, resourceType: string): void { + /** + * Adds default location filtering for a provider + * If more granular filtering is needed, use `setLocationSubset` instead (i.e. if the provider further filters locations based on features) + * @param wizardContext The context of the wizard + * @param provider The provider (i.e. 'Microsoft.Storage') + * @param resourceType The resource type (i.e. 'storageAccounts') + */ + public static addProviderForFiltering(wizardContext: T, provider: string, resourceType: string): void { this.setLocationSubset(wizardContext, getProviderLocations(wizardContext, provider, resourceType), provider); } - public static hasLocation(wizardContext: T): boolean { - return !!wizardContext._location; + /** + * Returns true if a location has been set on the context + */ + public static hasLocation(wizardContext: T): boolean { + return !!(wizardContext as T & ILocationWizardContextInternal)._location; } - public static getExtendedLocation(location: types.AzExtLocation): { location: string, extendedLocation?: ExtendedLocation } { + /** + * Used to convert a location into a home location and an extended location if the location passed in is an extended location. + * If the location passed in is not extended, then extendedLocation will be `undefined`. + * @param location location or extended location + */ + public static getExtendedLocation(location: AzExtLocation): { location: string, extendedLocation?: ExtendedLocation } { let locationName: string = location.name; let extendedLocation: ExtendedLocation | undefined; if (location.type === 'EdgeZone') { @@ -117,14 +175,25 @@ export class LocationListStep extends }; } - public static getAutoSelectLocation(wizardContext: T): types.AzExtLocation | undefined { - return wizardContext._autoSelectLocation; + /** + * Gets the `autoSelectLocation` for this wizard. This location will be automatically selected during prompting, if available. + * @param wizardContext The context of the wizard + */ + public static getAutoSelectLocation(wizardContext: T): AzExtLocation | undefined { + return (wizardContext as T & ILocationWizardContextInternal)._autoSelectLocation; } - public static async getLocation(wizardContext: T, provider?: string, supportsExtendedLocations?: boolean): Promise { - let location: types.AzExtLocation = nonNullProp(wizardContext, '_location'); + /** + * Gets the selected location for this wizard. + * @param wizardContext The context of the wizard + * @param provider If specified, this will check against that provider's supported locations and attempt to find a "related" location if the selected location is not supported. + * @param supportsExtendedLocations If set to true, the location returned may be an extended location, in which case the `extendedLocation` property should be added when creating a resource + */ + public static async getLocation(wizardContext: T, provider?: string, supportsExtendedLocations?: boolean): Promise { + const ctx = wizardContext as T & ILocationWizardContextInternal; + let location: AzExtLocation = nonNullProp(ctx, '_location'); - function warnAboutRelatedLocation(loc: types.AzExtLocation): void { + function warnAboutRelatedLocation(loc: AzExtLocation): void { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion ext.outputChannel.appendLog(vscode.l10n.t('WARNING: Provider "{0}" does not support location "{1}". Using "{2}" instead.', provider!, location.displayName, loc.displayName)); } @@ -148,10 +217,10 @@ export class LocationListStep extends const [allLocationsTask, providerLocationsMap] = this.getInternalVariables(wizardContext); const providerLocations = await providerLocationsMap.get(provider.toLowerCase()); if (providerLocations) { - function isSupportedByProvider(loc: types.AzExtLocation): boolean { + function isSupportedByProvider(loc: AzExtLocation): boolean { return !!providerLocations?.find(name => LocationListStep.locationMatchesName(loc, name)); } - function useProviderName(loc: types.AzExtLocation): types.AzExtLocation { + function useProviderName(loc: AzExtLocation): AzExtLocation { // Some providers prefer their version of the name over the standard one, so we'll create a shallow clone using theirs return { ...loc, name: nonNullValue(providerLocations?.find(name => LocationListStep.locationMatchesName(loc, name), 'providerName')) }; } @@ -162,7 +231,7 @@ export class LocationListStep extends const allLocations = await allLocationsTask; if (location.metadata?.pairedRegion) { - const pairedLocation: types.AzExtLocation | undefined = location.metadata?.pairedRegion + const pairedLocation: AzExtLocation | undefined = location.metadata?.pairedRegion .map(paired => allLocations.find(l => paired.name && LocationListStep.locationMatchesName(l, paired.name))) .find(pairedLoc => pairedLoc && isSupportedByProvider(pairedLoc)); if (pairedLocation) { @@ -190,7 +259,11 @@ export class LocationListStep extends return location; } - public static async getLocations(wizardContext: T): Promise { + /** + * Used to get locations. By passing in the context, we can ensure that Azure is only queried once for the entire wizard + * @param wizardContext The context of the wizard. + */ + public static async getLocations(wizardContext: T): Promise { const [allLocationsTask, providerLocationsMap] = this.getInternalVariables(wizardContext); const locationSubsets: string[][] = await Promise.all(providerLocationsMap.values()); // Filter to locations supported by every provider @@ -199,7 +272,10 @@ export class LocationListStep extends )); } - public static locationMatchesName(location: types.AzExtLocation, name: string): boolean { + /** + * Returns true if the given location matches the name + */ + public static locationMatchesName(location: AzExtLocation, name: string): boolean { name = LocationListStep.generalizeLocationName(name); return name === LocationListStep.generalizeLocationName(location.name) || name === LocationListStep.generalizeLocationName(location.displayName); } @@ -217,22 +293,23 @@ export class LocationListStep extends const picks = await this.getQuickPicks(wizardContext); - let pick: AgentQuickPickItem> | undefined; - if (wizardContext._autoSelectLocation) { - pick = picks.find(p => p.data.id === wizardContext._autoSelectLocation?.id); + let pick: AgentQuickPickItem> | undefined; + const ctx = wizardContext as T & ILocationWizardContextInternal; + if (ctx._autoSelectLocation) { + pick = picks.find(p => p.data.id === ctx._autoSelectLocation?.id); } - pick ??= await wizardContext.ui.showQuickPick(picks, options); + pick ??= await (wizardContext as T & { ui: IAzureAgentInput }).ui.showQuickPick(picks, options); - wizardContext._location = pick.data; - wizardContext.telemetry.properties.locationType = wizardContext._location.type; + ctx._location = pick.data; + wizardContext.telemetry.properties.locationType = ctx._location.type; } public shouldPrompt(wizardContext: T): boolean { - return !wizardContext._location; + return !(wizardContext as T & ILocationWizardContextInternal)._location; } - protected async getQuickPicks(wizardContext: T): Promise>[]> { - let locations: types.AzExtLocation[] = await LocationListStep.getLocations(wizardContext); + protected async getQuickPicks(wizardContext: T): Promise>[]> { + let locations: AzExtLocation[] = await LocationListStep.getLocations(wizardContext); locations = locations.sort(compareLocation); return locations.map(l => { @@ -250,16 +327,19 @@ export class LocationListStep extends return (name || '').toLowerCase().replace(/[^a-z0-9]/gi, ''); } - public static getQuickPickDescription?: (location: types.AzExtLocation) => string | undefined; + /** + * Implement this to set descriptions on location quick pick items. + */ + public static getQuickPickDescription?: (location: AzExtLocation) => string | undefined; } -async function getAllLocations(wizardContext: types.ILocationWizardContext): Promise { +async function getAllLocations(wizardContext: ILocationWizardContext): Promise { const client = await createSubscriptionsClient(wizardContext); const locations = await uiUtils.listAllIterator(client.subscriptions.listLocations(wizardContext.subscriptionId, { includeExtendedLocations: wizardContext.includeExtendedLocations })); - return locations.filter((l): l is types.AzExtLocation => !!(l.id && l.name && l.displayName)); + return locations.filter((l): l is AzExtLocation => !!(l.id && l.name && l.displayName)); } -async function getProviderLocations(wizardContext: types.ILocationWizardContext, provider: string, resourceType: string): Promise { +async function getProviderLocations(wizardContext: ILocationWizardContext, provider: string, resourceType: string): Promise { const rgClient = await createResourcesClient(wizardContext); const providerData = await rgClient.providers.get(provider); const resourceTypeData = providerData.resourceTypes?.find(rt => rt.resourceType?.toLowerCase() === resourceType.toLowerCase()); @@ -269,7 +349,7 @@ async function getProviderLocations(wizardContext: types.ILocationWizardContext, return nonNullProp(resourceTypeData, 'locations'); } -function compareLocation(l1: types.AzExtLocation, l2: types.AzExtLocation): number { +function compareLocation(l1: AzExtLocation, l2: AzExtLocation): number { if (!isRecommended(l1) && isRecommended(l2)) { return 1; } else if (isRecommended(l1) && !isRecommended(l2)) { @@ -279,7 +359,7 @@ function compareLocation(l1: types.AzExtLocation, l2: types.AzExtLocation): numb } } -function isRecommended(l: types.AzExtLocation): boolean { +function isRecommended(l: AzExtLocation): boolean { return l.metadata?.regionCategory?.toLowerCase() === 'recommended'; } diff --git a/azure/src/wizard/ResourceGroupCreateStep.ts b/azure/src/wizard/ResourceGroupCreateStep.ts index 1ae6b253c6..38174f475e 100644 --- a/azure/src/wizard/ResourceGroupCreateStep.ts +++ b/azure/src/wizard/ResourceGroupCreateStep.ts @@ -6,7 +6,7 @@ import { ActivityChildItem, ActivityChildType, activityErrorContext, activityFailContext, activityFailIcon, ActivityOutputType, AzureWizardExecuteStepWithActivityOutput, createContextValue, ExecuteActivityOutput, nonNullProp, nonNullValueAndProp, parseError } from '@microsoft/vscode-azext-utils'; import { randomUUID } from 'crypto'; import { l10n, MessageItem, Progress, TreeItemCollapsibleState } from 'vscode'; -import * as types from '../../index'; +import { IResourceGroupWizardContext } from './resourceGroupWizardTypes'; import { createResourcesClient } from '../clients'; import { resourcesProvider } from '../constants'; import { ext } from '../extensionVariables'; @@ -14,7 +14,10 @@ import { uiUtils } from '../utils/uiUtils'; import { LocationListStep } from './LocationListStep'; import { ResourceGroupListStep } from './ResourceGroupListStep'; -export class ResourceGroupCreateStep extends AzureWizardExecuteStepWithActivityOutput { +export class ResourceGroupCreateStep extends AzureWizardExecuteStepWithActivityOutput { + /** + * 100 + */ public priority: number = 100; public stepName: string = 'resourceGroupCreateStep'; @@ -109,7 +112,7 @@ export class ResourceGroupCreateStep extends AzureWizardExecuteStepWithActivityOutput { +class ResourceGroupNoCreatePermissionsSelectStep extends AzureWizardExecuteStepWithActivityOutput { public priority: number = 101; public stepName: string = 'resourceGroupNoCreatePermissionsSelectStep'; protected getOutputLogSuccess = (context: T) => l10n.t('Successfully selected existing resource group "{0}".', nonNullValueAndProp(context.resourceGroup, 'name')); diff --git a/azure/src/wizard/ResourceGroupListStep.ts b/azure/src/wizard/ResourceGroupListStep.ts index 0e11d80ff9..7e8c10b662 100644 --- a/azure/src/wizard/ResourceGroupListStep.ts +++ b/azure/src/wizard/ResourceGroupListStep.ts @@ -6,7 +6,7 @@ import type { ResourceGroup } from '@azure/arm-resources'; import { AzureWizardPromptStep, IAzureNamingRules, IAzureQuickPickItem, IAzureQuickPickOptions, IWizardOptions, nonNullProp } from '@microsoft/vscode-azext-utils'; import * as vscode from 'vscode'; -import * as types from '../../index'; +import { IResourceGroupWizardContext } from './resourceGroupWizardTypes'; import { createResourcesClient } from '../clients'; import { uiUtils } from '../utils/uiUtils'; import { LocationListStep } from './LocationListStep'; @@ -21,7 +21,7 @@ export const resourceGroupNamingRules: IAzureNamingRules = { invalidCharsRegExp: /[^a-zA-Z0-9\.\_\-\(\)]/ }; -export class ResourceGroupListStep extends AzureWizardPromptStep implements types.ResourceGroupListStep { +export class ResourceGroupListStep extends AzureWizardPromptStep { private _suppressCreate: boolean | undefined; public constructor(suppressCreate?: boolean) { @@ -29,7 +29,11 @@ export class ResourceGroupListStep this._suppressCreate = suppressCreate; } - public static async getResourceGroups(wizardContext: T): Promise { + /** + * Used to get existing resource groups. By passing in the context, we can ensure that Azure is only queried once for the entire wizard + * @param wizardContext The context of the wizard. + */ + public static async getResourceGroups(wizardContext: T): Promise { if (wizardContext.resourceGroupsTask === undefined) { const client = await createResourcesClient(wizardContext); wizardContext.resourceGroupsTask = uiUtils.listAllIterator(client.resourceGroups.list()); @@ -38,7 +42,11 @@ export class ResourceGroupListStep return await wizardContext.resourceGroupsTask; } - public static async isNameAvailable(wizardContext: T, name: string): Promise { + /** + * Checks existing resource groups in the wizard's subscription to see if the name is available. + * @param wizardContext The context of the wizard. + */ + public static async isNameAvailable(wizardContext: T, name: string): Promise { const resourceGroupsTask: Promise = ResourceGroupListStep.getResourceGroups(wizardContext); return !(await resourceGroupsTask).some((rg: ResourceGroup) => rg.name !== undefined && rg.name.toLowerCase() === name.toLowerCase()); } diff --git a/azure/src/wizard/ResourceGroupNameStep.ts b/azure/src/wizard/ResourceGroupNameStep.ts index b8ac8e191f..665eb876ac 100644 --- a/azure/src/wizard/ResourceGroupNameStep.ts +++ b/azure/src/wizard/ResourceGroupNameStep.ts @@ -5,10 +5,10 @@ import { AzureWizardPromptStep } from '@microsoft/vscode-azext-utils'; import * as vscode from 'vscode'; -import * as types from '../../index'; +import { IResourceGroupWizardContext } from './resourceGroupWizardTypes'; import { ResourceGroupListStep, resourceGroupNamingRules } from './ResourceGroupListStep'; -export class ResourceGroupNameStep extends AzureWizardPromptStep implements types.ResourceGroupNameStep { +export class ResourceGroupNameStep extends AzureWizardPromptStep { public async prompt(wizardContext: T): Promise { const suggestedName: string | undefined = wizardContext.relatedNameTask ? await wizardContext.relatedNameTask : undefined; wizardContext.newResourceGroupName = (await wizardContext.ui.showInputBox({ diff --git a/azure/src/wizard/ResourceGroupVerifyStep.ts b/azure/src/wizard/ResourceGroupVerifyStep.ts index 787dea9418..473579403c 100644 --- a/azure/src/wizard/ResourceGroupVerifyStep.ts +++ b/azure/src/wizard/ResourceGroupVerifyStep.ts @@ -6,12 +6,15 @@ import { ActivityChildItem, ActivityChildType, activityErrorContext, activityFailContext, activityFailIcon, AzureWizardExecuteStepWithActivityOutput, createContextValue, ExecuteActivityOutput, nonNullProp, parseError } from '@microsoft/vscode-azext-utils'; import { randomUUID } from 'crypto'; import { l10n, Progress, TreeItemCollapsibleState } from 'vscode'; -import * as types from '../../index'; +import { IResourceGroupWizardContext } from './resourceGroupWizardTypes'; import { createResourcesClient } from '../clients'; import { ext } from '../extensionVariables'; // See for more background: https://github.com/microsoft/vscode-azuretools/pull/1992#issue-3034841865 -export class ResourceGroupVerifyStep extends AzureWizardExecuteStepWithActivityOutput { +export class ResourceGroupVerifyStep extends AzureWizardExecuteStepWithActivityOutput { + /** + * 95 + */ public priority: number = 95; public stepName: string = 'resourceGroupVerifyStep'; diff --git a/azure/src/wizard/RoleAssignmentExecuteStep.ts b/azure/src/wizard/RoleAssignmentExecuteStep.ts index 3fef3ec99b..79d5bdd752 100644 --- a/azure/src/wizard/RoleAssignmentExecuteStep.ts +++ b/azure/src/wizard/RoleAssignmentExecuteStep.ts @@ -6,33 +6,56 @@ import { AzureWizardExecuteStep, AzureWizardExecuteStepWithActivityOutput, ExecuteActivityContext, nonNullValueAndProp, parseError } from '@microsoft/vscode-azext-utils'; import { randomUUID } from 'crypto'; import { l10n, Progress } from 'vscode'; -import * as types from '../../index'; +import { IResourceGroupWizardContext } from './resourceGroupWizardTypes'; import { createAuthorizationManagementClient } from '../clients'; export interface Role { + /** + * The scope of the operation or resource. Valid scopes are: subscription (format: + * '/subscriptions/{subscriptionId}'), resource group (format: + * '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}', or resource (format: + * '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/[{parentResourcePath}/]{resourceType}/{resourceName}' + */ scopeId: string | undefined; + /** + * The role definition id of the role to assign. This can be created using `createRoleId` + */ roleDefinitionId: string; + /** + * The name of the role definition to assign + */ roleDefinitionName: string; } -export class RoleAssignmentExecuteStep extends AzureWizardExecuteStep> { +export class RoleAssignmentExecuteStep extends AzureWizardExecuteStep> { + /** + * 900 + */ public priority: number; - public async execute(_wiardContext: types.IResourceGroupWizardContext & Partial, _progress: Progress<{ message?: string; increment?: number; }>): Promise { + public async execute(_wiardContext: IResourceGroupWizardContext & Partial, _progress: Progress<{ message?: string; increment?: number; }>): Promise { // nothing should execute, but we need shouldExecute to be true so that addExecuteSteps is called return Promise.resolve(undefined); } - public shouldExecute(_wizardContext: types.IResourceGroupWizardContext & Partial): boolean { + public shouldExecute(_wizardContext: IResourceGroupWizardContext & Partial): boolean { return true; } private roles: () => (Role[] | Promise | undefined); + /** + * @param getScopeId A function that returns the scope id for the role assignment. + * The scope ID is the Azure ID of the resource that we are granting access to such as a storage account. + * Example: `/subscriptions/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/rgName/providers/Microsoft.Storage/storageAccounts/resourceName` + * This typically won't exist until _after_ the wizard executes and the resource is created, so we need to pass in a function that returns the ID. + * If the scope ID is undefined, the step will throw an error. + * @param roles An array of roles. Each role is an object and include the ARM role definition id and name of the role definition. + * */ public constructor(roles: () => (Role[] | Promise | undefined), options?: { priority?: number }) { super(); this.roles = roles; this.priority = options?.priority ?? 900; } - public async addExecuteSteps(_context: types.IResourceGroupWizardContext & Partial): Promise>[]> { + public async addExecuteSteps(_context: IResourceGroupWizardContext & Partial): Promise>[]> { const roles = await this.roles(); const steps = []; for (const role of roles ?? []) { @@ -43,7 +66,7 @@ export class RoleAssignmentExecuteStep extends AzureWizardExecuteStep> extends AzureWizardExecuteStepWithActivityOutput { +class SingleRoleAssignmentExecuteStep> extends AzureWizardExecuteStepWithActivityOutput { public priority: number; stepName: string = 'RoleAssignmentExecuteStep'; diff --git a/azure/src/wizard/StorageAccountCreateStep.ts b/azure/src/wizard/StorageAccountCreateStep.ts index 3ad87e3e89..e3e6736ce3 100644 --- a/azure/src/wizard/StorageAccountCreateStep.ts +++ b/azure/src/wizard/StorageAccountCreateStep.ts @@ -6,18 +6,21 @@ import type { SkuName } from '@azure/arm-storage'; import { AzureWizardExecuteStepWithActivityOutput, nonNullProp } from '@microsoft/vscode-azext-utils'; import { l10n, Progress } from 'vscode'; -import * as types from '../../index'; +import { INewStorageAccountDefaults, IStorageAccountWizardContext } from './storageWizardTypes'; import { createStorageClient } from '../clients'; import { storageProvider } from '../constants'; import { LocationListStep } from './LocationListStep'; -export class StorageAccountCreateStep extends AzureWizardExecuteStepWithActivityOutput implements types.StorageAccountCreateStep { +export class StorageAccountCreateStep extends AzureWizardExecuteStepWithActivityOutput { stepName: string = 'StorageAccountCreateStep'; + /** + * 130 + */ public priority: number = 130; - private readonly _defaults: types.INewStorageAccountDefaults; + private readonly _defaults: INewStorageAccountDefaults; - public constructor(defaults: types.INewStorageAccountDefaults) { + public constructor(defaults: INewStorageAccountDefaults) { super(); this._defaults = defaults; } diff --git a/azure/src/wizard/StorageAccountListStep.ts b/azure/src/wizard/StorageAccountListStep.ts index 6d92891e23..585ce5eabe 100644 --- a/azure/src/wizard/StorageAccountListStep.ts +++ b/azure/src/wizard/StorageAccountListStep.ts @@ -6,11 +6,11 @@ import type { NetworkRuleSet, StorageAccount } from '@azure/arm-storage'; import { AzureWizardPromptStep, IAzureNamingRules, IAzureQuickPickItem, IAzureQuickPickOptions, IWizardOptions, nonNullProp, openUrl } from '@microsoft/vscode-azext-utils'; import * as vscode from 'vscode'; -import * as types from '../../index'; +import { INewStorageAccountDefaults, IStorageAccountWizardContext } from './storageWizardTypes'; import { createStorageClient } from '../clients'; import { storageProvider, storageProviderType } from '../constants'; import { uiUtils } from '../utils/uiUtils'; -import { LocationListStep } from './LocationListStep'; +import { AzExtLocation, LocationListStep } from './LocationListStep'; import { ResourceGroupListStep } from './ResourceGroupListStep'; import { StorageAccountCreateStep } from './StorageAccountCreateStep'; import { StorageAccountNameStep } from './StorageAccountNameStep'; @@ -22,51 +22,35 @@ export const storageAccountNamingRules: IAzureNamingRules = { lowercaseOnly: true }; -export enum StorageAccountKind { - Storage = 'Storage', - StorageV2 = 'StorageV2', - BlobStorage = 'BlobStorage', - BlockBlobStorage = 'BlockBlobStorage' -} +export { StorageAccountKind, StorageAccountPerformance, StorageAccountReplication } from './storageWizardTypes'; +import { StorageAccountKind, StorageAccountPerformance, StorageAccountReplication } from './storageWizardTypes'; -export enum StorageAccountPerformance { - Standard = 'Standard', - Premium = 'Premium' -} +export interface IStorageAccountFilters { + kind?: StorageAccountKind[]; + performance?: StorageAccountPerformance[]; + replication?: StorageAccountReplication[]; -export enum StorageAccountReplication { /** - * Locally redundant storage + * If specified, a 'learn more' option will be displayed to explain why some storage accounts were filtered */ - LRS = 'LRS', + learnMoreLink?: string; +} - /** - * Zone-redundant storage - */ - ZRS = 'ZRS', +export class StorageAccountListStep extends AzureWizardPromptStep { + private readonly _newAccountDefaults: INewStorageAccountDefaults; + private readonly _filters: IStorageAccountFilters; /** - * Geo-redundant storage + * @param createOptions Default options to use when creating a Storage Account + * @param filterOptions Optional filters used when listing Storage Accounts */ - GRS = 'GRS', - - /** - * Read-access geo-redundant storage - */ - RAGRS = 'RAGRS' -} - -export class StorageAccountListStep extends AzureWizardPromptStep implements types.StorageAccountListStep { - private readonly _newAccountDefaults: types.INewStorageAccountDefaults; - private readonly _filters: types.IStorageAccountFilters; - - public constructor(newAccountDefaults: types.INewStorageAccountDefaults, filters?: types.IStorageAccountFilters) { + public constructor(newAccountDefaults: INewStorageAccountDefaults, filters?: IStorageAccountFilters) { super(); this._newAccountDefaults = newAccountDefaults; this._filters = filters ?? {}; } - public static async isNameAvailable(wizardContext: T, name: string): Promise { + public static async isNameAvailable(wizardContext: T, name: string): Promise { const storageClient = await createStorageClient(wizardContext); return !!(await storageClient.storageAccounts.checkNameAvailability({ name, type: storageProviderType })).nameAvailable; } @@ -113,7 +97,7 @@ export class StorageAccountListStep extends AzureNameStep { +export class StorageAccountNameStep extends AzureNameStep { public async prompt(wizardContext: T): Promise { const client = await createStorageClient(wizardContext); diff --git a/azure/src/wizard/UserAssignedIdentityCreateStep.ts b/azure/src/wizard/UserAssignedIdentityCreateStep.ts index abb0dc756a..9ce7fdce81 100644 --- a/azure/src/wizard/UserAssignedIdentityCreateStep.ts +++ b/azure/src/wizard/UserAssignedIdentityCreateStep.ts @@ -6,7 +6,7 @@ import { type ManagedServiceIdentityClient } from '@azure/arm-msi'; import { AzureWizardExecuteStepWithActivityOutput, nonNullProp, nonNullValueAndProp } from '@microsoft/vscode-azext-utils'; import { l10n, Progress } from 'vscode'; -import * as types from '../../index'; +import { IResourceGroupWizardContext } from './resourceGroupWizardTypes'; import { createManagedServiceIdentityClient } from '../clients'; import { storageProvider } from '../constants'; import { LocationListStep } from './LocationListStep'; @@ -20,8 +20,11 @@ import { UserAssignedIdentityNameStep } from './UserAssignedIdentityNameStep'; * But since we are appending "-identities" to the resource group name and that has the same constraints and a 90 character limit, * we don't need to do any verification **/ -export class UserAssignedIdentityCreateStep extends AzureWizardExecuteStepWithActivityOutput { - // We should create this immediately after the resource group is created +export class UserAssignedIdentityCreateStep extends AzureWizardExecuteStepWithActivityOutput { + /** + * We should create this immediately after the resource group is created + * 101 + */ public priority: number = 101; public stepName: string = 'UserAssignedIdentityCreateStep'; diff --git a/azure/src/wizard/UserAssignedIdentityListStep.ts b/azure/src/wizard/UserAssignedIdentityListStep.ts index b69d51a0cf..9eb7522698 100644 --- a/azure/src/wizard/UserAssignedIdentityListStep.ts +++ b/azure/src/wizard/UserAssignedIdentityListStep.ts @@ -6,7 +6,7 @@ import type { Identity } from '@azure/arm-msi'; import { AzureWizardPromptStep, IAzureQuickPickItem, IAzureQuickPickOptions, IWizardOptions } from '@microsoft/vscode-azext-utils'; import * as vscode from 'vscode'; -import * as types from '../../index'; +import { IResourceGroupWizardContext } from './resourceGroupWizardTypes'; import { createManagedServiceIdentityClient } from '../clients'; import { IdentityProvider, UserAssignedIdentityResourceType } from '../constants'; import { uiUtils } from '../utils/uiUtils'; @@ -15,7 +15,7 @@ import { ResourceGroupListStep } from './ResourceGroupListStep'; import { UserAssignedIdentityCreateStep } from './UserAssignedIdentityCreateStep'; import { UserAssignedIdentityNameStep } from './UserAssignedIdentityNameStep'; -export class UserAssignedIdentityListStep extends AzureWizardPromptStep { +export class UserAssignedIdentityListStep extends AzureWizardPromptStep { private _suppressCreate: boolean | undefined; public constructor(suppressCreate?: boolean) { diff --git a/azure/src/wizard/UserAssignedIdentityNameStep.ts b/azure/src/wizard/UserAssignedIdentityNameStep.ts index cf818ea98d..4223fa2fae 100644 --- a/azure/src/wizard/UserAssignedIdentityNameStep.ts +++ b/azure/src/wizard/UserAssignedIdentityNameStep.ts @@ -5,10 +5,10 @@ import { AzureWizardPromptStep, randomUtils, validationUtils } from '@microsoft/vscode-azext-utils'; import * as vscode from 'vscode'; -import * as types from '../../index'; +import { IResourceGroupWizardContext } from './resourceGroupWizardTypes'; import { createManagedServiceIdentityClient } from '../clients'; -export class UserAssignedIdentityNameStep extends AzureWizardPromptStep { +export class UserAssignedIdentityNameStep extends AzureWizardPromptStep { public async prompt(context: T): Promise { let suggestedName: string | undefined; const rgName: string | undefined = context.resourceGroup?.name ?? context.newResourceGroupName; @@ -58,7 +58,7 @@ export class UserAssignedIdentityNameStep { + static async isNameAvailable(context: IResourceGroupWizardContext, rgName: string, identityName: string): Promise { try { const client = await createManagedServiceIdentityClient(context); return !await client.userAssignedIdentities.get(rgName, identityName); @@ -67,7 +67,7 @@ export class UserAssignedIdentityNameStep { + static async tryGenerateRelatedName(context: IResourceGroupWizardContext, rgName: string): Promise { const newName: string = `${rgName}-identities-${randomUtils.getRandomHexString(6)}`; const isNameAvailable: boolean = await UserAssignedIdentityNameStep.isNameAvailable(context, rgName, newName); return isNameAvailable ? newName : undefined; diff --git a/azure/src/wizard/VerifyProvidersStep.ts b/azure/src/wizard/VerifyProvidersStep.ts index ad7cfcee20..9e23c529e9 100644 --- a/azure/src/wizard/VerifyProvidersStep.ts +++ b/azure/src/wizard/VerifyProvidersStep.ts @@ -5,14 +5,22 @@ import { AzureWizardExecuteStep, IParsedError, ISubscriptionActionContext, maskUserInfo, parseError } from '@microsoft/vscode-azext-utils'; import { l10n, Progress } from 'vscode'; -import * as types from '../../index'; import { createResourcesClient } from '../clients'; import { delay } from '../utils/delay'; -export class VerifyProvidersStep extends AzureWizardExecuteStep implements types.VerifyProvidersStep { +/** + * Checks to see if providers (i.e. 'Microsoft.Web') are registered and registers them if they're not + */ +export class VerifyProvidersStep extends AzureWizardExecuteStep { + /** + * 90 + */ public priority: number = 90; private _providers: string[]; + /** + * @param providers List of providers to verify + */ public constructor(providers: string[]) { super(); this._providers = providers; diff --git a/azure/src/wizard/resourceGroupWizardTypes.ts b/azure/src/wizard/resourceGroupWizardTypes.ts new file mode 100644 index 0000000000..9ffe5bcd8a --- /dev/null +++ b/azure/src/wizard/resourceGroupWizardTypes.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { Identity } from '@azure/arm-msi'; +import type { ResourceGroup } from '@azure/arm-resources'; +import type { IAzureUserInput, IRelatedNameWizardContext, ISubscriptionActionContext } from '@microsoft/vscode-azext-utils'; + +/** + * Currently no location-specific properties on the wizard context, but keeping this interface for backwards compatibility and ease of use + * Instead, use static methods on `LocationListStep` like `getLocation` and `setLocationSubset` + */ +export interface ILocationWizardContext extends ISubscriptionActionContext { + includeExtendedLocations?: boolean; +} + +export interface IResourceGroupWizardContext extends ILocationWizardContext, IRelatedNameWizardContext { + /** + * Internal value indicating the last resource group name checked with `ResourceGroupVerifyStep`. + * This name does not indicate a successful outcome from the verification step, it only indicates that the check has taken place. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + _lastResourceGroupNameVerified?: string; + + /** + * The resource group to use for new resources. + * If an existing resource group is picked, this value will be defined after `ResourceGroupListStep.prompt` occurs + * If a new resource group is picked, this value will be defined after the `execute` phase of the 'create' subwizard + */ + resourceGroup?: ResourceGroup; + + /** + * The task used to get existing resource groups. + * By specifying this in the context, we can ensure that Azure is only queried once for the entire wizard + */ + resourceGroupsTask?: Promise; + + newResourceGroupName?: string; + + /** + * By default, users will be prompted to select an existing resource group if creating one fails with a 403 error. Set this to `true` to prevent that behavior + */ + suppress403Handling?: boolean; + + /** + * Will automatically be generated by UserAssignedIdentityCreateStep if not specified by the user + */ + newManagedIdentityName?: string; + + /** + * The managed identity that will be assigned to the resource such as a function app or container app + * If you need to grant access to a resource, such as a storage account or SQL database, you can use this managed identity to create a role assignment + * with the RoleAssignmentExecuteStep + */ + managedIdentity?: Identity; + + ui: IAzureUserInput; +} diff --git a/azure/src/wizard/storageWizardTypes.ts b/azure/src/wizard/storageWizardTypes.ts new file mode 100644 index 0000000000..55a35b890b --- /dev/null +++ b/azure/src/wizard/storageWizardTypes.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { StorageAccount } from '@azure/arm-storage'; +import type { IResourceGroupWizardContext } from './resourceGroupWizardTypes'; + +export interface IStorageAccountWizardContext extends IResourceGroupWizardContext { + /** + * The storage account to use. + * If an existing storage account is picked, this value will be defined after `StorageAccountListStep.prompt` occurs + * If a new storage account is picked, this value will be defined after the `execute` phase of the 'create' subwizard + */ + storageAccount?: StorageAccount; + + newStorageAccountName?: string; + /** + * This controls whether the storage account can generate connection strings. + * This should be disabled for storage accounts that are using managed identity only. + */ + disableSharedKeyAccess?: boolean; +} + +export enum StorageAccountKind { + Storage = 'Storage', + StorageV2 = 'StorageV2', + BlobStorage = 'BlobStorage', + BlockBlobStorage = 'BlockBlobStorage' +} + +export enum StorageAccountPerformance { + Standard = 'Standard', + Premium = 'Premium' +} + +export enum StorageAccountReplication { + /** + * Locally redundant storage + */ + LRS = 'LRS', + + /** + * Zone-redundant storage + */ + ZRS = 'ZRS', + + /** + * Geo-redundant storage + */ + GRS = 'GRS', + + /** + * Read-access geo-redundant storage + */ + RAGRS = 'RAGRS' +} + +export interface INewStorageAccountDefaults { + kind: StorageAccountKind; + performance: StorageAccountPerformance; + replication: StorageAccountReplication; +} diff --git a/azure/test/request.test.ts b/azure/test/request.test.ts index 3db558a7b2..9fb297c26f 100644 --- a/azure/test/request.test.ts +++ b/azure/test/request.test.ts @@ -6,8 +6,7 @@ import { createTestActionContext } from '@microsoft/vscode-azext-utils'; import * as assert from 'assert'; import * as http from 'http'; -import * as types from '../index'; -import { sendRequestWithTimeout } from '../src/createAzureClient'; +import { type AzExtPipelineResponse, sendRequestWithTimeout } from '../src/createAzureClient'; import { assertThrowsAsync } from './assertThrowsAsync'; type ResponseData = { statusCode: number; contentType?: string; body?: string; } | ((response: http.ServerResponse) => void); @@ -17,7 +16,7 @@ suite('request', () => { let server: http.Server; let testResponses: ResponseData[] = []; - async function sendTestRequest(...responses: ResponseData[]): Promise { + async function sendTestRequest(...responses: ResponseData[]): Promise { testResponses = responses; return await sendRequestWithTimeout(await createTestActionContext(), { method: 'GET', url, allowInsecureConnection: true }, 2000, undefined); } diff --git a/azure/tsconfig.json b/azure/tsconfig.json index 09fe887743..287682d917 100644 --- a/azure/tsconfig.json +++ b/azure/tsconfig.json @@ -3,8 +3,6 @@ "compilerOptions": { "outDir": "dist/esm", "rootDir": ".", - "declaration": false, "strict": true, - "skipLibCheck": true, // TODO: webpack typings issue }, }