diff --git a/appservice/package-lock.json b/appservice/package-lock.json index dd20aff96a..43fc2245df 100644 --- a/appservice/package-lock.json +++ b/appservice/package-lock.json @@ -14,10 +14,8 @@ "@azure/arm-appservice": "^13.0.2", "@azure/arm-operationalinsights": "^8.0.1", "@azure/arm-resourcegraph": "^4.0.0", - "@azure/arm-resources": "^5.0.0", - "@azure/arm-subscriptions": "^5.0.0", + "@azure/arm-resources-subscriptions": "^2.0.1", "@azure/ms-rest-azure-env": "^2.0.0", - "@azure/ms-rest-js": "^2.3.0", "@azure/storage-blob": "^12.3.0", "@microsoft/vscode-azext-azureutils": "^0.3.7", "@microsoft/vscode-azext-utils": "^0.4.0", @@ -212,23 +210,6 @@ "node": ">=12.0.0" } }, - "node_modules/@azure/arm-subscriptions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@azure/arm-subscriptions/-/arm-subscriptions-5.0.0.tgz", - "integrity": "sha512-kka1Gsy5fvQvYbe3gRsMl2hYCFMdQRHuOSSRUAsQUwAEqIJCu/hLZ/CNKcYusIMrA0SWzrjlFYVklo/uUKYolg==", - "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-client": "^1.0.0", - "@azure/core-lro": "^2.2.0", - "@azure/core-paging": "^1.2.0", - "@azure/core-rest-pipeline": "^1.1.0", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/@azure/core-asynciterator-polyfill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz", @@ -8156,20 +8137,6 @@ "tslib": "^2.2.0" } }, - "@azure/arm-subscriptions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@azure/arm-subscriptions/-/arm-subscriptions-5.0.0.tgz", - "integrity": "sha512-kka1Gsy5fvQvYbe3gRsMl2hYCFMdQRHuOSSRUAsQUwAEqIJCu/hLZ/CNKcYusIMrA0SWzrjlFYVklo/uUKYolg==", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-client": "^1.0.0", - "@azure/core-lro": "^2.2.0", - "@azure/core-paging": "^1.2.0", - "@azure/core-rest-pipeline": "^1.1.0", - "tslib": "^2.2.0" - } - }, "@azure/core-asynciterator-polyfill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz", diff --git a/appservice/package.json b/appservice/package.json index 9258845878..6c637420ab 100644 --- a/appservice/package.json +++ b/appservice/package.json @@ -37,10 +37,8 @@ "@azure/arm-appservice": "^13.0.2", "@azure/arm-operationalinsights": "^8.0.1", "@azure/arm-resourcegraph": "^4.0.0", - "@azure/arm-resources": "^5.0.0", - "@azure/arm-subscriptions": "^5.0.0", + "@azure/arm-resources-subscriptions": "^2.0.1", "@azure/ms-rest-azure-env": "^2.0.0", - "@azure/ms-rest-js": "^2.3.0", "@azure/storage-blob": "^12.3.0", "@microsoft/vscode-azext-azureutils": "^0.3.7", "@microsoft/vscode-azext-utils": "^0.4.0", diff --git a/azure/index.d.ts b/azure/index.d.ts index a20ff761ed..bd399a67ae 100644 --- a/azure/index.d.ts +++ b/azure/index.d.ts @@ -5,14 +5,15 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import type { ExtendedLocation, ResourceGroup } from '@azure/arm-resources'; import type { Location } from '@azure/arm-resources-subscriptions'; -import type { Environment } from '@azure/ms-rest-azure-env'; -import type { HttpOperationResponse, RequestPrepareOptions, ServiceClient } from '@azure/ms-rest-js'; -import type { PagedAsyncIterableIterator } from '@azure/core-paging'; -import { Disposable, Progress } from 'vscode'; -import type { AzExtParentTreeItem, AzExtServiceClientCredentials, AzExtServiceClientCredentialsT1, AzExtServiceClientCredentialsT2, AzExtTreeItem, AzureNameStep, AzureWizardExecuteStep, AzureWizardPromptStep, IActionContext, IAzureNamingRules, IAzureQuickPickItem, IAzureQuickPickOptions, IRelatedNameWizardContext, ISubscriptionActionContext, ISubscriptionContext, IWizardOptions, UIExtensionVariables } from '@microsoft/vscode-azext-utils'; -import { ExtendedLocation, ResourceGroup } from '@azure/arm-resources'; import type { StorageAccount } 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, AzureWizardPromptStep, IActionContext, IAzureNamingRules, IAzureQuickPickItem, IAzureQuickPickOptions, IRelatedNameWizardContext, ISubscriptionActionContext, ISubscriptionContext, IWizardOptions, UIExtensionVariables } from '@microsoft/vscode-azext-utils'; +import type { Progress } from 'vscode'; export type OpenInPortalOptions = { /** @@ -24,53 +25,13 @@ export type OpenInPortalOptions = { /** * Implement this class to display resources under a standard subscription tree item */ -export abstract class SubscriptionTreeItemBase extends AzExtParentTreeItem { +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 - see `TestAzureAccount` in @microsoft/vscode-azext-dev package - */ - 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. * @@ -231,7 +192,7 @@ export declare class ResourceGroupListStep extends AzureWizardPromptStep { +export declare class ResourceGroupNameStep extends AzureWizardPromptStep { public prompt(wizardContext: T): Promise; public shouldPrompt(wizardContext: T): boolean; } @@ -348,26 +309,10 @@ export interface IAzureUtilsExtensionVariables extends UIExtensionVariables { */ export declare function registerAzureUtilsExtensionVariables(extVars: IAzureUtilsExtensionVariables): void; -export interface IMinimumServiceClientOptions { - acceptLanguage?: string, - baseUri?: string; - /** - * Pass in endpoint as a workaround for https://github.com/Azure/azure-sdk-for-js/issues/20651. - * Value should be the same as `baseUri`. - */ - endpoint?: string; - userAgent?: string | ((defaultUserAgent: string) => string); - - /** - * NOTE: Using "any" to allow for the use of different versions of "@azure/ms-rest-js", which are largely compatible for our purposes - */ - requestPolicyFactories?: any[] | ((defaultRequestPolicyFactories: any[]) => (void | any[])); -} - /** * Credential type to be used for creating generic http rest clients */ -export type AzExtGenericCredentials = AzExtServiceClientCredentialsT1 | AzExtServiceClientCredentialsT2 | AzExtServiceClientCredentials; +export type AzExtGenericCredentials = AzExtServiceClientCredentialsT2 | AzExtServiceClientCredentials; export type AzExtGenericClientInfo = AzExtGenericCredentials | { credentials: AzExtGenericCredentials; environment: Environment; } | undefined; /** @@ -376,18 +321,18 @@ export type AzExtGenericClientInfo = AzExtGenericCredentials | { credentials: Az * 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 function createGenericClient(context: IActionContext, clientInfo: AzExtGenericClientInfo): Promise; +export declare function createGenericClient(context: IActionContext, clientInfo: AzExtGenericClientInfo | undefined): Promise; -export type AzExtRequestPrepareOptions = RequestPrepareOptions & { rejectUnauthorized?: boolean } +export type AzExtRequestPrepareOptions = PipelineRequestOptions & { rejectUnauthorized?: boolean } /** * 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 function sendRequestWithTimeout(context: IActionContext, options: AzExtRequestPrepareOptions, timeout: number, clientInfo: AzExtGenericClientInfo): Promise; +export declare function sendRequestWithTimeout(context: IActionContext, options: AzExtRequestPrepareOptions, timeout: number, clientInfo: AzExtGenericClientInfo): Promise; -export type AzExtClientType = new (credentials: AzExtServiceClientCredentials, subscriptionId: string, options?: IMinimumServiceClientOptions) => T; +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 @@ -397,23 +342,23 @@ export type AzExtClientContext = ISubscriptionActionContext | [IActionContext, I /** * Converts `AzExtClientContext` into a single object: `ISubscriptionActionContext` */ -export function parseClientContext(clientContext: AzExtClientContext): 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 function createAzureClient(context: AzExtClientContext, clientType: AzExtClientType): T; +export declare function createAzureClient(context: AzExtClientContext, clientType: AzExtClientType): T; -export type AzExtSubscriptionClientType = new (credentials: AzExtServiceClientCredentials, options?: IMinimumServiceClientOptions) => 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 function createAzureSubscriptionClient(context: AzExtClientContext, clientType: AzExtSubscriptionClientType): T; +export declare function createAzureSubscriptionClient(context: AzExtClientContext, clientType: AzExtSubscriptionClientType): T; export declare namespace uiUtils { export function listAllIterator(iterator: PagedAsyncIterableIterator): Promise @@ -427,5 +372,5 @@ interface ParsedAzureResourceId { resourceName: string; } -export function parseAzureResourceId(id: string): ParsedAzureResourceId; -export function getResourceGroupFromId(id: string): string; +export declare function parseAzureResourceId(id: string): ParsedAzureResourceId; +export declare function getResourceGroupFromId(id: string): string; diff --git a/azure/package.json b/azure/package.json index 2bda8043ef..6c80fbe58b 100644 --- a/azure/package.json +++ b/azure/package.json @@ -1,65 +1,67 @@ { - "name": "@microsoft/vscode-azext-azureutils", - "author": "Microsoft Corporation", - "version": "0.3.7", - "description": "Common Azure utils for developing Azure extensions for VS Code", - "tags": [ - "azure", - "vscode" - ], - "keywords": [ - "azure", - "vscode" - ], - "main": "out/src/index.js", - "types": "index.d.ts", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/Microsoft/vscode-azuretools" - }, - "bugs": { - "url": "https://github.com/Microsoft/vscode-azuretools/issues" - }, - "homepage": "https://github.com/Microsoft/vscode-azuretools/blob/main/azure/README.md", - "scripts": { - "build": "tsc -p ./", - "prepack": "tsc -p ./", - "compile": "tsc -watch -p ./", - "lint": "eslint --ext .ts .", - "lint-fix": "eslint --ext .ts . --fix", - "pretest": "npm run build", - "test": "node ./out/test/runTest.js" - }, - "dependencies": { - "@azure/arm-resources": "^5.0.0", - "@azure/arm-resources-profile-2020-09-01-hybrid": "^2.0.0", - "@azure/arm-resources-subscriptions": "^2.0.0", - "@azure/arm-storage": "^17.0.0", - "@azure/arm-storage-profile-2020-09-01-hybrid": "^2.0.0", - "@azure/ms-rest-js": "^2.2.1", - "@microsoft/vscode-azext-utils": "^0.4.0", - "semver": "^7.3.7", - "vscode-nls": "^5.0.1" - }, - "devDependencies": { - "@azure/core-paging": "^1.2.1", - "@azure/ms-rest-azure-env": "^2.0.0", - "@azure/ms-rest-nodeauth": "^3.1.1", - "@microsoft/eslint-config-azuretools": "^0.1.0", - "@microsoft/vscode-azext-dev": "^0.1.4", - "@types/mocha": "^7.0.2", - "@types/node": "^14.0.0", - "@types/semver": "^7.3.9", - "@types/vscode": "1.64.0", - "@typescript-eslint/eslint-plugin": "^4.28.3", - "@vscode/test-electron": "^2.1.5", - "eslint": "^7.19.0", - "eslint-plugin-import": "^2.22.1", - "glob": "^7.1.6", - "mocha": "^9.1.3", - "mocha-junit-reporter": "^2.0.2", - "mocha-multi-reporters": "^1.1.7", - "typescript": "^4.9.4" - } + "name": "@microsoft/vscode-azext-azureutils", + "author": "Microsoft Corporation", + "version": "0.4.0", + "description": "Common Azure utils for developing Azure extensions for VS Code", + "tags": [ + "azure", + "vscode" + ], + "keywords": [ + "azure", + "vscode" + ], + "main": "out/src/index.js", + "types": "index.d.ts", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/vscode-azuretools" + }, + "bugs": { + "url": "https://github.com/Microsoft/vscode-azuretools/issues" + }, + "homepage": "https://github.com/Microsoft/vscode-azuretools/blob/main/azure/README.md", + "scripts": { + "build": "tsc -p ./", + "prepack": "tsc -p ./", + "compile": "tsc -watch -p ./", + "lint": "eslint --ext .ts .", + "lint-fix": "eslint --ext .ts . --fix", + "pretest": "npm run build", + "test": "node ./out/test/runTest.js" + }, + "dependencies": { + "@azure/arm-resources": "^5.0.0", + "@azure/arm-resources-profile-2020-09-01-hybrid": "^2.0.0", + "@azure/arm-resources-subscriptions": "^2.0.0", + "@azure/arm-storage": "^18.0.0", + "@azure/arm-storage-profile-2020-09-01-hybrid": "^2.0.0", + "@azure/core-client": "^1.6.0", + "@azure/core-rest-pipeline": "^1.9.0", + "@microsoft/vscode-azext-utils": "file:../utils/microsoft-vscode-azext-utils-0.4.0.tgz", + "semver": "^7.3.7", + "uuid": "^9.0.0", + "vscode-nls": "^5.0.1" + }, + "devDependencies": { + "@azure/core-auth": "^1.3.2", + "@azure/core-paging": "^1.2.1", + "@azure/ms-rest-azure-env": "^2.0.0", + "@microsoft/eslint-config-azuretools": "^0.1.0", + "@microsoft/vscode-azext-dev": "^0.1.4", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.0", + "@types/semver": "^7.3.9", + "@types/vscode": "1.64.0", + "@typescript-eslint/eslint-plugin": "^4.28.3", + "@vscode/test-electron": "^2.1.5", + "eslint": "^7.19.0", + "eslint-plugin-import": "^2.22.1", + "glob": "^7.1.6", + "mocha": "^9.1.3", + "mocha-junit-reporter": "^2.0.2", + "mocha-multi-reporters": "^1.1.7", + "typescript": "^4.9.4" + } } diff --git a/azure/src/GenericServiceClient.ts b/azure/src/GenericServiceClient.ts deleted file mode 100644 index 4e4a3d01ed..0000000000 --- a/azure/src/GenericServiceClient.ts +++ /dev/null @@ -1,31 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { HttpOperationResponse, RequestPrepareOptions, ServiceClient, WebResourceLike } from '@azure/ms-rest-js'; -import * as vscode from 'vscode'; -import * as types from '../index'; - -export class GenericServiceClient extends ServiceClient { - constructor(credentials: types.AzExtGenericCredentials | undefined, options: types.IMinimumServiceClientOptions) { - super(credentials, options); - this.baseUri = options.baseUri?.endsWith('/') ? options.baseUri.slice(0, -1) : options.baseUri; - } - - public async sendRequest(options: RequestPrepareOptions | WebResourceLike): Promise { - if (this.baseUri && options.url && !options.url.startsWith('http')) { - if (!options.url.startsWith('/')) { - options.url = `/${options.url}`; - } - - options.url = this.baseUri + options.url; - } - - options['baseUrl'] = this.baseUri; - options.headers ||= {}; - options.headers['accept-language'] = vscode.env.language; - - return await super.sendRequest(options); - } -} diff --git a/azure/src/azure-account.api.d.ts b/azure/src/azure-account.api.d.ts index 563de4d97e..a24627ee22 100644 --- a/azure/src/azure-account.api.d.ts +++ b/azure/src/azure-account.api.d.ts @@ -4,44 +4,43 @@ *--------------------------------------------------------------------------------------------*/ import type { Subscription } from '@azure/arm-resources-subscriptions'; -import { TokenCredential } from '@azure/core-auth'; -import { Environment } from '@azure/ms-rest-azure-env'; -import type { TokenCredentialsBase } from '@azure/ms-rest-nodeauth'; -import { ReadStream } from 'fs'; -import { CancellationToken, Event, Progress, Terminal } from 'vscode'; +import type { TokenCredential } from '@azure/core-auth'; +import type { Environment } from '@azure/ms-rest-azure-env'; +import type { ReadStream } from 'fs'; +import type { CancellationToken, Event, Progress, Terminal } from 'vscode'; export type AzureLoginStatus = 'Initializing' | 'LoggingIn' | 'LoggedIn' | 'LoggedOut'; export interface AzureAccountExtensionApi { - readonly apiVersion: string; - readonly status: AzureLoginStatus; - readonly filters: AzureResourceFilter[]; - readonly sessions: AzureSession[]; - readonly subscriptions: AzureSubscription[]; - readonly onStatusChanged: Event; - readonly onFiltersChanged: Event; - readonly onSessionsChanged: Event; - readonly onSubscriptionsChanged: Event; - readonly waitForFilters: () => Promise; - readonly waitForLogin: () => Promise; - readonly waitForSubscriptions: () => Promise; - createCloudShell(os: 'Linux' | 'Windows'): CloudShell; + readonly apiVersion: string; + readonly status: AzureLoginStatus; + readonly filters: AzureResourceFilter[]; + readonly sessions: AzureSession[]; + readonly subscriptions: AzureSubscription[]; + readonly onStatusChanged: Event; + readonly onFiltersChanged: Event; + readonly onSessionsChanged: Event; + readonly onSubscriptionsChanged: Event; + readonly waitForFilters: () => Promise; + readonly waitForLogin: () => Promise; + readonly waitForSubscriptions: () => Promise; + createCloudShell(os: 'Linux' | 'Windows'): CloudShell; } export interface AzureSession { - readonly environment: Environment; - readonly userId: string; - readonly tenantId: string; - - /** - * The credentials object for azure-sdk-for-js modules https://github.com/azure/azure-sdk-for-js - */ - readonly credentials2: TokenCredentialsBase & TokenCredential; + readonly environment: Environment; + readonly userId: string; + readonly tenantId: string; + + /** + * The credentials object for azure-sdk-for-js modules https://github.com/azure/azure-sdk-for-js + */ + readonly credentials2: TokenCredential; } export interface AzureSubscription { - readonly session: AzureSession; - readonly subscription: Subscription; + readonly session: AzureSession; + readonly subscription: Subscription; } export type AzureResourceFilter = AzureSubscription; @@ -49,16 +48,16 @@ export type AzureResourceFilter = AzureSubscription; export type CloudShellStatus = 'Connecting' | 'Connected' | 'Disconnected'; export interface UploadOptions { - contentLength?: number; - progress?: Progress<{ message?: string; increment?: number }>; - token?: CancellationToken; + contentLength?: number; + progress?: Progress<{ message?: string; increment?: number }>; + token?: CancellationToken; } export interface CloudShell { - readonly status: CloudShellStatus; - readonly onStatusChanged: Event; - readonly waitForConnection: () => Promise; - readonly terminal: Promise; - readonly session: Promise; - readonly uploadFile: (filename: string, stream: ReadStream, options?: UploadOptions) => Promise; + readonly status: CloudShellStatus; + readonly onStatusChanged: Event; + readonly waitForConnection: () => Promise; + readonly terminal: Promise; + readonly session: Promise; + readonly uploadFile: (filename: string, stream: ReadStream, options?: UploadOptions) => Promise; } diff --git a/azure/src/createAzureClient.ts b/azure/src/createAzureClient.ts index 86734df3d8..e9f58461fe 100644 --- a/azure/src/createAzureClient.ts +++ b/azure/src/createAzureClient.ts @@ -3,16 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BaseRequestPolicy, BasicAuthenticationCredentials, HttpOperationResponse, RequestPolicy, RequestPolicyFactory, RequestPolicyOptions, RestError, ServiceClient, TokenCredentials, WebResource, WebResourceLike } from '@azure/ms-rest-js'; +import { ServiceClient } from '@azure/core-client'; +import { createPipelineFromOptions, createPipelineRequest, Pipeline, PipelineOptions, PipelinePolicy, PipelineRequest, PipelineResponse, RetryPolicyOptions, SendRequest } from '@azure/core-rest-pipeline'; +import { appendExtensionUserAgent, AzExtTreeItem, IActionContext, ISubscriptionActionContext, ISubscriptionContext } from '@microsoft/vscode-azext-utils'; import { randomUUID } from 'crypto'; -import { Agent } from 'http'; import { Agent as HttpsAgent } from 'https'; import * as vscode from "vscode"; import * as types from '../index'; -import type { GenericServiceClient } from './GenericServiceClient'; -import { localize } from './localize'; -import { parseJson, removeBom } from './utils/parseJson'; -import { appendExtensionUserAgent, AzExtTreeItem, IActionContext, ISubscriptionActionContext, ISubscriptionContext, maskValue, parseError } from '@microsoft/vscode-azext-utils'; export type InternalAzExtClientContext = ISubscriptionActionContext | [IActionContext, ISubscriptionContext | AzExtTreeItem]; @@ -35,41 +32,33 @@ export function parseClientContext(clientContext: InternalAzExtClientContext): I } } -export function createAzureClient(clientContext: InternalAzExtClientContext, clientType: types.AzExtClientType): T { +export function createAzureClient(clientContext: InternalAzExtClientContext, clientType: types.AzExtClientType): T { const context = parseClientContext(clientContext); return new clientType(context.credentials, context.subscriptionId, { - acceptLanguage: vscode.env.language, - baseUri: context.environment.resourceManagerEndpointUrl, - endpoint: context.environment.resourceManagerEndpointUrl, - userAgent: appendExtensionUserAgent, - requestPolicyFactories: (defaultFactories: RequestPolicyFactory[]) => addAzExtFactories(context, context.credentials, defaultFactories), + //endpoint: context.environment.resourceManagerEndpointUrl, + //userAgent: appendExtensionUserAgent, }); } -export function createAzureSubscriptionClient(clientContext: InternalAzExtClientContext, clientType: types.AzExtSubscriptionClientType): T { +export function createAzureSubscriptionClient(clientContext: InternalAzExtClientContext, clientType: types.AzExtSubscriptionClientType): T { const context = parseClientContext(clientContext); return new clientType(context.credentials, { - acceptLanguage: vscode.env.language, - baseUri: context.environment.resourceManagerEndpointUrl, endpoint: context.environment.resourceManagerEndpointUrl, - userAgent: appendExtensionUserAgent, - requestPolicyFactories: (defaultFactories: RequestPolicyFactory[]) => addAzExtFactories(context, context.credentials, defaultFactories), + pipeline: createAzExtPipeline(context), }); } -export async function sendRequestWithTimeout(context: IActionContext, options: types.AzExtRequestPrepareOptions, timeout: number, clientInfo: types.AzExtGenericClientInfo): Promise { - let request: WebResource = new WebResource(); - request = request.prepare(options); - request.timeout = timeout; +export async function sendRequestWithTimeout(context: IActionContext, options: types.AzExtRequestPrepareOptions, timeout: number, clientInfo: types.AzExtGenericClientInfo): Promise { + const request: PipelineRequest = createPipelineRequest({ + ...options, + timeout + }); - if (options.rejectUnauthorized !== undefined) { - request.agentSettings = { - http: new Agent(), - https: new HttpsAgent({ rejectUnauthorized: options.rejectUnauthorized }) - } + if (options.rejectUnauthorized) { + request.agent = new HttpsAgent({ rejectUnauthorized: options.rejectUnauthorized }); } - const client: GenericServiceClient = await createGenericClient(context, clientInfo, { noRetryPolicy: true }); + const client = await createGenericClient(context, clientInfo, { noRetryPolicy: true }); return await client.sendRequest(request); } @@ -77,168 +66,159 @@ interface IGenericClientOptions { noRetryPolicy?: boolean; } -export async function createGenericClient(context: IActionContext, clientInfo: types.AzExtGenericClientInfo, options?: IGenericClientOptions): Promise { +export async function createGenericClient(context: IActionContext, clientInfo: types.AzExtGenericClientInfo | undefined, options?: IGenericClientOptions): Promise { let credentials: types.AzExtGenericCredentials | undefined; - let baseUri: string | undefined; + let endpoint: string | undefined; if (clientInfo && 'credentials' in clientInfo) { credentials = clientInfo.credentials; - baseUri = clientInfo.environment.resourceManagerEndpointUrl; + endpoint = clientInfo.environment.resourceManagerEndpointUrl; } else { credentials = clientInfo; } - const gsc: typeof import('./GenericServiceClient') = await import('./GenericServiceClient'); + const retryOptions: RetryPolicyOptions | undefined = options?.noRetryPolicy ? { maxRetries: 0 } : undefined; - return new gsc.GenericServiceClient(credentials, { - baseUri, - endpoint: baseUri, - userAgent: appendExtensionUserAgent, - requestPolicyFactories: (defaultFactories: RequestPolicyFactory[]) => addAzExtFactories(context, credentials, defaultFactories), - noRetryPolicy: options?.noRetryPolicy + const client = new ServiceClient({ + credential: credentials, + endpoint, + pipeline: createAzExtPipeline( + context, + endpoint, + { + retryOptions, + } + ), }); + + return client; } -function addAzExtFactories(context: IActionContext, credentials: types.AzExtGenericCredentials | undefined, defaultFactories: RequestPolicyFactory[]): RequestPolicyFactory[] { - // NOTE: Factories at the end of the array are executed first, and we want these to happen before the deserialization factory - defaultFactories.push( - { - create: (nextPolicy, options): RequestPolicy => new RemoveBOMPolicy(nextPolicy, options) - }, - { - create: (nextPolicy, options): RequestPolicy => new MissingContentTypePolicy(nextPolicy, options) - } - ); +function createAzExtPipeline(context: IActionContext, endpoint?: string, options?: PipelineOptions): Pipeline { + const pipeline = createPipelineFromOptions({ + ...options, + userAgentOptions: { userAgentPrefix: appendExtensionUserAgent() }, + }); + // Policies to apply before the request + pipeline.addPolicy(new AcceptLanguagePolicy(), { phase: 'Serialize' }); if (vscode.env.isTelemetryEnabled) { - defaultFactories.push( - { - create: (nextPolicy, options): RequestPolicy => new CorrelationIdPolicy(nextPolicy, options, context) - } - ) + pipeline.addPolicy(new CorrelationIdPolicy(context), { phase: 'Serialize' }); + } + if (endpoint) { + pipeline.addPolicy(new AddEndpointPolicy(endpoint), { phase: 'Serialize' }); } - // We want these to execute last - defaultFactories.unshift( - { - create: (nextPolicy, options): RequestPolicy => new MaskCredentialsPolicy(nextPolicy, options, credentials) - }, - { - create: (nextPolicy, options): RequestPolicy => new StatusCodePolicy(nextPolicy, options) - } - ); + // Policies to apply after the response + // pipeline.addPolicy(new MissingContentTypePolicy(), { phase: 'Deserialize' }); + // TODO: MissingContentTypePolicy literally conflicts with RemoveBOMPolicy + // pipeline.addPolicy(new RemoveBOMPolicy(), { phase: 'Deserialize', beforePolicies: [MissingContentTypePolicy.Name] }); + // pipeline.addPolicy(new StatusCodePolicy(), { phase: StatusCodePolicy.policyPhase }); - return defaultFactories; + return pipeline; } -const contentTypeName: string = 'Content-Type'; - /** * Automatically add id to correlate our telemetry with the platform team's telemetry */ -class CorrelationIdPolicy extends BaseRequestPolicy { - private _context: IActionContext; - constructor(nextPolicy: RequestPolicy, requestPolicyOptions: RequestPolicyOptions, context: IActionContext) { - super(nextPolicy, requestPolicyOptions); - this._context = context; +class CorrelationIdPolicy implements PipelinePolicy { + public readonly name = 'CorrelationIdPolicy'; + + constructor(private readonly context: IActionContext) { } - public async sendRequest(request: WebResourceLike): Promise { + public async sendRequest(request: PipelineRequest, next: SendRequest): Promise { const headerName = 'x-ms-correlation-request-id'; - const id: string = this._context.telemetry.properties[headerName] ||= randomUUID(); + const id: string = this.context.telemetry.properties[headerName] ||= randomUUID(); request.headers.set(headerName, id); - return await this._nextPolicy.sendRequest(request); + return await next.sendRequest(request); } } /** * Removes the BOM character if it exists in bodyAsText for a json response, to prevent a parse error */ -class RemoveBOMPolicy extends BaseRequestPolicy { - constructor(nextPolicy: RequestPolicy, requestPolicyOptions: RequestPolicyOptions) { - super(nextPolicy, requestPolicyOptions); - } +// class RemoveBOMPolicy implements PipelinePolicy { +// public readonly name = 'RemoveBOMPolicy'; - public async sendRequest(request: WebResourceLike): Promise { - const response: HttpOperationResponse = await this._nextPolicy.sendRequest(request); - const contentType: string | undefined = response.headers.get(contentTypeName); - if (contentType && /json/i.test(contentType) && response.bodyAsText) { - response.bodyAsText = removeBom(response.bodyAsText); - } - return response; - } -} +// public async sendRequest(request: PipelineRequest, next: SendRequest): Promise { +// const response: PipelineResponse = await next(request); +// const contentType: string | undefined = response.headers.get(contentTypeName); +// if (contentType && /json/i.test(contentType) && response.bodyAsText) { +// response.bodyAsText = removeBom(response.bodyAsText); +// } +// return response; +// } +// } + +// const contentTypeName: string = 'Content-Type'; /** * The Azure SDK will assume "JSON" if no content-type is specified, which can cause false-positive parse errors. * This will be a little smarter and try to detect if it's json or generic data */ -class MissingContentTypePolicy extends BaseRequestPolicy { - constructor(nextPolicy: RequestPolicy, requestPolicyOptions: RequestPolicyOptions) { - super(nextPolicy, requestPolicyOptions); - } - - public async sendRequest(request: WebResourceLike): Promise { - const response: HttpOperationResponse = await this._nextPolicy.sendRequest(request); - if (!response.headers.get(contentTypeName) && response.bodyAsText) { - try { - parseJson(response.bodyAsText); - response.headers.set(contentTypeName, 'application/json'); - } catch { - response.headers.set(contentTypeName, 'application/octet-stream'); - } - } - return response; - } -} +// class MissingContentTypePolicy implements PipelinePolicy { +// public static readonly Name = 'MissingContentTypePolicy'; +// public readonly name = MissingContentTypePolicy.Name; + +// public async sendRequest(request: PipelineRequest, next: SendRequest): Promise { +// const response: PipelineResponse = await next(request); +// if (!response.headers.get(contentTypeName) && response.bodyAsText) { +// try { +// parseJson(response.bodyAsText); +// response.headers.set(contentTypeName, 'application/json'); +// } catch { +// response.headers.set(contentTypeName, 'application/octet-stream'); +// } +// } +// return response; +// } +// } /** * The Azure SDK will only throw errors for bad status codes if it has an "operationSpec", but none of our "generic" requests will have that */ -class StatusCodePolicy extends BaseRequestPolicy { - constructor(nextPolicy: RequestPolicy, requestPolicyOptions: RequestPolicyOptions) { - super(nextPolicy, requestPolicyOptions); - } - - public async sendRequest(request: WebResourceLike): Promise { - const response: HttpOperationResponse = await this._nextPolicy.sendRequest(request); - if (!request.operationSpec && (response.status < 200 || response.status >= 300)) { - const errorMessage: string = response.bodyAsText ? - parseError(response.parsedBody || response.bodyAsText).message : - localize('unexpectedStatusCode', 'Unexpected status code: {0}', response.status); - throw new RestError(errorMessage, undefined, response.status, request, response, response.bodyAsText); - } else { - return response; - } +// class StatusCodePolicy implements PipelinePolicy { +// public readonly name = 'StatusCodePolicy'; +// public static readonly policyPhase: PipelinePhase = 'Deserialize'; + +// public async sendRequest(request: PipelineRequest, next: SendRequest): Promise { +// const response: PipelineResponse = await next(request); +// if (!request.operationSpec && (response.status < 200 || response.status >= 300)) { +// const errorMessage: string = response.bodyAsText ? +// parseError(response.bodyAsText).message : +// localize('unexpectedStatusCode', 'Unexpected status code: {0}', response.status); +// throw new RestError(errorMessage, { code: response.status.toString(), statusCode: response.status, request, response }); +// } else { +// return response; +// } +// } +// } + +// Add the "Accept-Language" header +class AcceptLanguagePolicy implements PipelinePolicy { + public readonly name = 'AcceptLanguagePolicy'; + + public async sendRequest(request: PipelineRequest, next: SendRequest): Promise { + request.headers.set('Accept-Language', vscode.env.language); + return await next(request); } } -/** - * In the highly unlikely event that a request error includes the original credentials used for the request, - * this policy will make sure those credentials get masked in the error message - */ -class MaskCredentialsPolicy extends BaseRequestPolicy { - private _credentials: types.AzExtGenericCredentials | undefined; - constructor(nextPolicy: RequestPolicy, requestPolicyOptions: RequestPolicyOptions, credentials: types.AzExtGenericCredentials | undefined,) { - super(nextPolicy, requestPolicyOptions); - this._credentials = credentials; - } +// Adds the endpoint to the request URL, if it is not present +class AddEndpointPolicy implements PipelinePolicy { + public readonly name = 'AddEndpointPolicy'; - public async sendRequest(request: WebResourceLike): Promise { - try { - return await this._nextPolicy.sendRequest(request); - } catch (error) { - const pe = parseError(error); - if (this._credentials) { - const tokenOrPassword = (>this._credentials).token || (>this._credentials).password; - - const maskedMessage = maskValue(pe.message, tokenOrPassword); - const maskedErrorType = maskValue(pe.errorType, tokenOrPassword); - if (pe.message !== maskedMessage || pe.errorType !== maskedErrorType) { - throw Object.assign(new Error(maskedMessage), { code: maskedErrorType }); - } + public constructor(private readonly endpoint: string) { } + + public async sendRequest(request: PipelineRequest, next: SendRequest): Promise { + if (this.endpoint && request.url && !request.url.startsWith('http')) { + if (!request.url.startsWith('/')) { + request.url = `/${request.url}`; } - throw error; + request.url = this.endpoint + request.url; } + + return await next(request); } } diff --git a/azure/src/index.ts b/azure/src/index.ts index 0b0535d352..720e9f952d 100644 --- a/azure/src/index.ts +++ b/azure/src/index.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ export * from './createAzureClient'; +export { registerAzureUtilsExtensionVariables } from './extensionVariables'; export * from './openInPortal'; -export * from './tree/AzureAccountTreeItemBase'; export * from './tree/SubscriptionTreeItemBase'; export * from './utils/parseAzureResourceId'; export * from './utils/uiUtils'; @@ -17,6 +17,5 @@ export * from './wizard/StorageAccountCreateStep'; export * from './wizard/StorageAccountListStep'; export * from './wizard/StorageAccountNameStep'; export * from './wizard/VerifyProvidersStep'; -export { registerAzureUtilsExtensionVariables } from './extensionVariables'; // 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/tree/AzureAccountTreeItemBase.ts b/azure/src/tree/AzureAccountTreeItemBase.ts deleted file mode 100644 index dbbb79590d..0000000000 --- a/azure/src/tree/AzureAccountTreeItemBase.ts +++ /dev/null @@ -1,274 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as semver from 'semver'; -import { commands, Disposable, Extension, extensions, MessageItem, ProgressLocation, ThemeIcon, window } from 'vscode'; -import * as types from '../../index'; -import { AzureAccountExtensionApi, AzureLoginStatus, AzureResourceFilter } from '../azure-account.api'; -import { localize } from '../localize'; -import { getIconPath } from './IconPath'; -import { SubscriptionTreeItemBase } from './SubscriptionTreeItemBase'; -import { AzExtServiceClientCredentials, nonNullProp, nonNullValue, UserCancelledError, registerEvent, AzureWizardPromptStep, AzExtParentTreeItem, AzExtTreeItem, GenericTreeItem, addExtensionValueToMask, IActionContext, ISubscriptionActionContext, TreeItemIconPath, ISubscriptionContext } from '@microsoft/vscode-azext-utils'; - -const signInLabel: string = localize('signInLabel', 'Sign in to Azure...'); -const createAccountLabel: string = localize('createAccountLabel', 'Create an Azure Account...'); -const createStudentAccountLabel: string = localize('createStudentAccount', 'Create an Azure for Students Account...'); -const selectSubscriptionsLabel: string = localize('noSubscriptions', 'Select Subscriptions...'); -const signInCommandId: string = 'azure-account.login'; -const createAccountCommandId: string = 'azure-account.createAccount'; -const createStudentAccountCommandId: string = 'azure-account.createStudentAccount'; -const selectSubscriptionsCommandId: string = 'azure-account.selectSubscriptions'; -const azureAccountExtensionId: string = 'ms-vscode.azure-account'; -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'; - public readonly contextValue: string = AzureAccountTreeItemBase.contextValue; - public readonly label: string = 'Azure'; - public childTypeLabel: string = localize('subscription', 'subscription'); - public autoSelectInTreeItemPicker: boolean = true; - public disposables: Disposable[] = []; - public suppressMaskLabel: boolean = true; - - private _azureAccountTask: Promise; - private _subscriptionTreeItems: SubscriptionTreeItemBase[] | undefined; - private _testAccount: AzureAccountExtensionApi | undefined; - - constructor(parent?: AzExtParentTreeItem, testAccount?: AzureAccountExtensionApi) { - super(parent); - this._testAccount = testAccount; - this._azureAccountTask = this.loadAzureAccount(testAccount); - } - - //#region Methods implemented by base class - public abstract createSubscriptionTreeItem(root: ISubscriptionContext): SubscriptionTreeItemBase | Promise; - //#endregion - - public get iconPath(): TreeItemIconPath { - return getIconPath('azure'); - } - - public dispose(): void { - Disposable.from(...this.disposables).dispose(); - } - - public hasMoreChildrenImpl(): boolean { - return false; - } - - public async loadMoreChildrenImpl(_clearCache: boolean, context: IActionContext): Promise { - let azureAccount: AzureAccountResult = await this._azureAccountTask; - if (typeof azureAccount === 'string') { - // Refresh the AzureAccount, to handle Azure account extension installation after the previous refresh - this._azureAccountTask = this.loadAzureAccount(this._testAccount); - azureAccount = await this._azureAccountTask; - } - - if (typeof azureAccount === 'string') { - context.telemetry.properties.accountStatus = azureAccount; - const label: string = azureAccount === 'notInstalled' ? - localize('installAzureAccount', 'Install Azure Account Extension...') : - localize('updateAzureAccount', 'Update Azure Account Extension to at least version "{0}"...', minAccountExtensionVersion); - const iconPath: TreeItemIconPath = new ThemeIcon('warning'); - const result: AzExtTreeItem = new GenericTreeItem(this, { label, commandId: extensionOpenCommand, contextValue: 'azureAccount' + azureAccount, includeInTreeItemPicker: true, iconPath }); - result.commandArgs = [azureAccountExtensionId]; - return [result]; - } - - context.telemetry.properties.accountStatus = azureAccount.status; - const existingSubscriptions: SubscriptionTreeItemBase[] = this._subscriptionTreeItems ? this._subscriptionTreeItems : []; - this._subscriptionTreeItems = []; - - const contextValue: string = 'azureCommand'; - if (azureAccount.status === 'Initializing' || azureAccount.status === 'LoggingIn') { - return [new GenericTreeItem(this, { - label: azureAccount.status === 'Initializing' ? localize('loadingTreeItem', 'Loading...') : localize('signingIn', 'Waiting for Azure sign-in...'), - commandId: signInCommandId, - contextValue, - id: signInCommandId, - iconPath: new ThemeIcon('loading~spin') - })]; - } else if (azureAccount.status === 'LoggedOut') { - const studentAccountTreeItem =new GenericTreeItem(this, { - label: createStudentAccountLabel, - commandId: 'azureResourceGroups.openUrl', - contextValue, - id: createStudentAccountCommandId, - iconPath: new ThemeIcon('mortar-board'), - includeInTreeItemPicker: true}); - - studentAccountTreeItem.commandArgs = ['https://aka.ms/student-account']; - - return [ - new GenericTreeItem(this, { label: signInLabel, commandId: signInCommandId, contextValue, id: signInCommandId, iconPath: new ThemeIcon('sign-in'), includeInTreeItemPicker: true }), - new GenericTreeItem(this, { label: createAccountLabel, commandId: createAccountCommandId, contextValue, id: createAccountCommandId, iconPath: new ThemeIcon('add'), includeInTreeItemPicker: true }), - studentAccountTreeItem - ]; - } - - await azureAccount.waitForFilters(); - - if (azureAccount.filters.length === 0) { - return [ - new GenericTreeItem(this, { label: selectSubscriptionsLabel, commandId: selectSubscriptionsCommandId, contextValue, id: selectSubscriptionsCommandId, includeInTreeItemPicker: true }) - ]; - } else { - this._subscriptionTreeItems = await Promise.all(azureAccount.filters.map(async (filter: AzureResourceFilter) => { - const existingTreeItem: SubscriptionTreeItemBase | undefined = existingSubscriptions.find(ti => ti.id === filter.subscription.id); - if (existingTreeItem) { - // Return existing treeItem (which might have many 'cached' tree items underneath it) rather than creating a brand new tree item every time - return existingTreeItem; - } else { - addExtensionValueToMask( - filter.subscription.id, - filter.subscription.subscriptionId, - filter.subscription.displayName, - filter.session.userId, - filter.session.tenantId, - ); - - // these properties don't exist on TokenCredentials, but do exist on DeviceTokenCredentials - // addExtensionValueToMask gracefully handles `undefined` input by ignoring it - addExtensionValueToMask( - filter.session.credentials2.clientId, - filter.session.credentials2.domain - ); - - // filter.subscription.id is the The fully qualified ID of the subscription (For example, /subscriptions/00000000-0000-0000-0000-000000000000) and should be used as the tree item's id for the purposes of OpenInPortal - // filter.subscription.subscriptionId is just the guid and is used in all other cases when creating clients for managing Azure resources - const subscriptionId: string = nonNullProp(filter.subscription, 'subscriptionId'); - return await this.createSubscriptionTreeItem({ - credentials: filter.session.credentials2, - subscriptionDisplayName: nonNullProp(filter.subscription, 'displayName'), - subscriptionId, - subscriptionPath: nonNullProp(filter.subscription, 'id'), - tenantId: filter.session.tenantId, - userId: filter.session.userId, - environment: filter.session.environment, - isCustomCloud: filter.session.environment.name === 'AzureCustomCloud' - }); - } - })); - return this._subscriptionTreeItems; - } - } - - public async getIsLoggedIn(): Promise { - const azureAccount: AzureAccountResult = await this._azureAccountTask; - return typeof azureAccount !== 'string' && azureAccount.status === 'LoggedIn'; - } - - public async getSubscriptionPromptStep(context: Partial & IActionContext): Promise | undefined> { - const subscriptionNodes: SubscriptionTreeItemBase[] = await this.ensureSubscriptionTreeItems(context); - if (subscriptionNodes.length === 1) { - Object.assign(context, subscriptionNodes[0].subscription); - return undefined; - } else { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const me: AzureAccountTreeItemBase = this; - class SubscriptionPromptStep extends AzureWizardPromptStep { - public async prompt(): Promise { - const ti: SubscriptionTreeItemBase = await me.treeDataProvider.showTreeItemPicker(SubscriptionTreeItemBase.contextValue, context, me); - Object.assign(context, ti.subscription); - } - public shouldPrompt(): boolean { return !(context).subscriptionId; } - } - return new SubscriptionPromptStep(); - } - } - - public async pickTreeItemImpl(_expectedContextValues: (string | RegExp)[]): Promise { - const azureAccount: AzureAccountResult = await this._azureAccountTask; - if (typeof azureAccount !== 'string' && (azureAccount.status === 'LoggingIn' || azureAccount.status === 'Initializing')) { - const title: string = localize('waitingForAzureSignin', 'Waiting for Azure sign-in...'); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await window.withProgress({ location: ProgressLocation.Notification, title }, async (): Promise => await azureAccount!.waitForSubscriptions()); - } - - return undefined; - } - - public compareChildrenImpl(item1: AzExtTreeItem, item2: AzExtTreeItem): number { - if (item1 instanceof GenericTreeItem && item2 instanceof GenericTreeItem) { - return 0; // already sorted - } else { - return super.compareChildrenImpl(item1, item2); - } - } - - private async loadAzureAccount(azureAccount: AzureAccountExtensionApi | undefined): Promise { - if (!azureAccount) { - const extension: Extension | undefined = extensions.getExtension(azureAccountExtensionId); - if (extension) { - try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (semver.lt(extension.packageJSON.version, minAccountExtensionVersion)) { - return 'needsUpdate'; - } - } catch { - // ignore and assume extension is up to date - } - - if (!extension.isActive) { - await extension.activate(); - } - - azureAccount = extension.exports; - } - } - - if (azureAccount) { - registerEvent('azureAccount.onFiltersChanged', azureAccount.onFiltersChanged, async (context) => { - context.errorHandling.suppressDisplay = true; - context.telemetry.suppressIfSuccessful = true; - await this.refresh(context); - }); - registerEvent('azureAccount.onStatusChanged', azureAccount.onStatusChanged, async (context: IActionContext, status: AzureLoginStatus) => { - context.errorHandling.suppressDisplay = true; - context.telemetry.suppressIfSuccessful = true; - // Ignore status change to 'LoggedIn' and wait for the 'onFiltersChanged' event to fire instead - // (so that the tree stays in 'Loading...' state until the filters are actually ready) - if (status !== 'LoggedIn') { - await this.refresh(context); - } - }); - await commands.executeCommand('setContext', 'isAzureAccountInstalled', true); - return azureAccount; - } else { - return 'notInstalled'; - } - } - - private async ensureSubscriptionTreeItems(context: IActionContext): Promise { - const azureAccount: AzureAccountResult = await this._azureAccountTask; - if (typeof azureAccount === 'string') { - let message: string; - let stepName: string; - if (azureAccount === 'notInstalled') { - stepName = 'requiresAzureAccount'; - message = localize('requiresAzureAccount', "This functionality requires installing the Azure Account extension."); - } else { - stepName = 'requiresUpdateToAzureAccount'; - message = localize('requiresUpdateToAzureAccount', 'This functionality requires updating the Azure Account extension to at least version "{0}".', minAccountExtensionVersion); - } - - const viewInMarketplace: MessageItem = { title: localize('viewInMarketplace', "View in Marketplace") }; - if (await context.ui.showWarningMessage(message, { stepName }, viewInMarketplace) === viewInMarketplace) { - await commands.executeCommand(extensionOpenCommand, azureAccountExtensionId); - } - - throw new UserCancelledError(`${stepName}|viewInMarketplace`); - } - - if (!this._subscriptionTreeItems) { - await this.getCachedChildren(context); - } - - return nonNullValue(this._subscriptionTreeItems, 'subscriptionTreeItems'); - } -} diff --git a/azure/src/wizard/LocationListStep.ts b/azure/src/wizard/LocationListStep.ts index 0fb2c53d99..5178e9eb0b 100644 --- a/azure/src/wizard/LocationListStep.ts +++ b/azure/src/wizard/LocationListStep.ts @@ -5,13 +5,12 @@ import type { ExtendedLocation } from '@azure/arm-resources'; import type { Location } from '@azure/arm-resources-subscriptions'; +import { AzureWizardPromptStep, IActionContext, IAzureQuickPickItem, IAzureQuickPickOptions, nonNullProp, nonNullValue } from '@microsoft/vscode-azext-utils'; import * as types from '../../index'; import { createResourcesClient, createSubscriptionsClient } from '../clients'; import { resourcesProvider } from '../constants'; -import { AzureWizardPromptStep, IActionContext, IAzureQuickPickItem, IAzureQuickPickOptions } from '@microsoft/vscode-azext-utils'; -import { localize } from '../localize'; import { ext } from '../extensionVariables'; -import { nonNullProp, nonNullValue } from '@microsoft/vscode-azext-utils'; +import { localize } from '../localize'; import { uiUtils } from '../utils/uiUtils'; interface ILocationWizardContextInternal extends types.ILocationWizardContext { diff --git a/azure/test/request.test.ts b/azure/test/request.test.ts index b93ae295da..525bb36f41 100644 --- a/azure/test/request.test.ts +++ b/azure/test/request.test.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BasicAuthenticationCredentials, HttpOperationResponse, Serializer, TokenCredentials, WebResource } from '@azure/ms-rest-js'; +import type { PipelineResponse } from '@azure/core-rest-pipeline'; +import { createTestActionContext } from '@microsoft/vscode-azext-dev'; import * as assert from 'assert'; import * as http from 'http'; -import { createTestActionContext } from '@microsoft/vscode-azext-dev'; -import { createGenericClient, sendRequestWithTimeout } from '../src/createAzureClient'; +import { sendRequestWithTimeout } from '../src/createAzureClient'; import { assertThrowsAsync } from './assertThrowsAsync'; type ResponseData = { status: number; contentType?: string; body?: string; } | ((response: http.ServerResponse) => void); @@ -17,7 +17,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 }, 2000, undefined); } @@ -53,23 +53,23 @@ suite('request', () => { test('200', async () => { const response = await sendTestRequest({ status: 200 }); - assert.strictEqual(response.parsedBody, undefined); + assert.strictEqual(JSON.parse(response.bodyAsText as string), undefined); }); test('200, text body, no content type', async () => { const response = await sendTestRequest({ status: 200, body: 'Hello World!' }); - assert.strictEqual(response.parsedBody, undefined); + assert.strictEqual(JSON.parse(response.bodyAsText as string), undefined); }); test('200, json body, no content type', async () => { const response = await sendTestRequest({ status: 200, body: '{ "data": "Hello World!" }' }); // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - assert.strictEqual(response.parsedBody.data, 'Hello World!'); + assert.strictEqual(JSON.parse(response.bodyAsText as string), 'Hello World!'); }); test('200, text body, text content type', async () => { const response = await sendTestRequest({ status: 200, body: 'cant parse this', contentType: 'text/plain' }); - assert.strictEqual(response.parsedBody, undefined); + assert.strictEqual(JSON.parse(response.bodyAsText as string), undefined); }); test('200, text body, json content type', async () => { @@ -78,25 +78,25 @@ suite('request', () => { test('200, json body, text content type', async () => { const response = await sendTestRequest({ status: 200, body: '{ "data": "Hello World!" }', contentType: 'text/plain' }); - assert.strictEqual(response.parsedBody, undefined); + assert.strictEqual(JSON.parse(response.bodyAsText as string), undefined); }); test('200, json body, json content type', async () => { const response = await sendTestRequest({ status: 200, body: '{ "data": "Hello World!" }', contentType: 'application/json' }); // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - assert.strictEqual(response.parsedBody.data, 'Hello World!'); + assert.strictEqual(JSON.parse(response.bodyAsText as string).data, 'Hello World!'); }); test('200, json body, no content type, with bom', async () => { const response = await sendTestRequest({ status: 200, body: `\ufeff{ "data": "Hello World!" }` }); // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - assert.strictEqual(response.parsedBody.data, 'Hello World!'); + assert.strictEqual(JSON.parse(response.bodyAsText as string).data, 'Hello World!'); }); test('200, json body, json content type, with bom', async () => { const response = await sendTestRequest({ status: 200, body: `\ufeff{ "data": "Hello World!" }`, contentType: 'application/json' }); // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - assert.strictEqual(response.parsedBody.data, 'Hello World!'); + assert.strictEqual(JSON.parse(response.bodyAsText as string).data, 'Hello World!'); }); test('400', async () => { @@ -115,38 +115,22 @@ suite('request', () => { await assertThrowsAsync(async () => await sendTestRequest(res => res.destroy()), /socket hang up/i); }); - test('operationSpec with unexpected error', async () => { - testResponses = [{ status: 404, body: 'oops' }]; - - const request = new WebResource(url); - request.operationSpec = { httpMethod: "GET", responses: { 200: {}, default: {} }, serializer: new Serializer() }; - const client = await createGenericClient(await createTestActionContext(), undefined); - await assertThrowsAsync(async () => await client.sendRequest(request), /oops/); - }); - - test('operationSpec with expected error', async () => { - testResponses = [{ status: 404, body: 'oops' }]; + //test('operationSpec with unexpected error', async () => { + // testResponses = [{ status: 404, body: 'oops' }]; - const request = new WebResource(url); - request.operationSpec = { httpMethod: "GET", responses: { 200: {}, 404: {}, default: {} }, serializer: new Serializer() }; - const client = await createGenericClient(await createTestActionContext(), undefined); - const response = await client.sendRequest(request); - assert.strictEqual(response.parsedBody, undefined); - }); - - test('Basic credentials are masked in error message', async () => { - const password: string = 'notActuallyCredentials'; - testResponses = [{ status: 404, body: password }]; + // const request = createPipelineRequest(url); + // request.operationSpec = { httpMethod: "GET", responses: { 200: {}, default: {} }, serializer: new Serializer() }; + // const client = await createGenericClient(await createTestActionContext(), undefined); + // await assertThrowsAsync(async () => await client.sendRequest(request), /oops/); + // }); - const client = await createGenericClient(await createTestActionContext(), new BasicAuthenticationCredentials('userName', password)); - await assertThrowsAsync(async () => await client.sendRequest({ method: 'GET', url }), (err: Error) => err.message === '---'); - }); + // test('operationSpec with expected error', async () => { + // testResponses = [{ status: 404, body: 'oops' }]; - test('Token credentials are masked in error message', async () => { - const token: string = 'notActuallyCredentials'; - testResponses = [{ status: 404, body: token }]; - - const client = await createGenericClient(await createTestActionContext(), new TokenCredentials(token)); - await assertThrowsAsync(async () => await client.sendRequest({ method: 'GET', url }), (err: Error) => err.message === '---'); - }); + // const request = new WebResource(url); + // request.operationSpec = { httpMethod: "GET", responses: { 200: {}, 404: {}, default: {} }, serializer: new Serializer() }; + // const client = await createGenericClient(await createTestActionContext(), undefined); + // const response = await client.sendRequest(request); + // assert.strictEqual(JSON.parse(response.bodyAsText as string), undefined); + // }); }); diff --git a/utils/index.d.ts b/utils/index.d.ts index 3a56ad1c98..147127a87c 100644 --- a/utils/index.d.ts +++ b/utils/index.d.ts @@ -7,7 +7,7 @@ import type { Environment } from '@azure/ms-rest-azure-env'; import { CancellationToken, CancellationTokenSource, Disposable, Event, ExtensionContext, FileChangeEvent, FileChangeType, FileStat, FileSystemProvider, FileType, InputBoxOptions, MarkdownString, MessageItem, MessageOptions, OpenDialogOptions, OutputChannel, Progress, QuickPickItem, QuickPickOptions as VSCodeQuickPickOptions, TextDocumentShowOptions, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, TreeView, Uri } from 'vscode'; -import { TargetPopulation } from 'vscode-tas-client'; +//import { TargetPopulation } from 'vscode-tas-client'; import { AzureExtensionApi, AzureExtensionApiProvider, GetApiOptions } from './api'; import type { Activity, ActivityTreeItemOptions, AppResource, OnErrorActivityData, OnProgressActivityData, OnStartActivityData, OnSuccessActivityData } from './hostapi'; // This must remain `import type` or else a circular reference will result import { AzureSubscription } from './hostapi.v2'; @@ -149,26 +149,10 @@ export interface ITreeItemPickerContext extends IActionContext { } /** - * Loose type to use for T1 and T2 versions of "@azure/ms-rest-js". The Azure Account extension returns + * Loose type to use for T2 versions of Azure credentials. The Azure Account extension returns * credentials that will satisfy both T1 and T2 requirements */ -export type AzExtServiceClientCredentials = AzExtServiceClientCredentialsT1 & AzExtServiceClientCredentialsT2; - -/** - * Loose interface to allow for the use of different versions of "@azure/ms-rest-js" - * There's several cases where we don't control which "credentials" interface gets used, causing build errors even though the functionality itself seems to be compatible - * For example: https://github.com/Azure/azure-sdk-for-js/issues/10045 - * Used specifically for T1 Azure SDKs - */ -export interface AzExtServiceClientCredentialsT1 { - /** - * Signs a request with the Authentication header. - * - * @param {WebResourceLike} webResource The WebResourceLike/request to be signed. - * @returns {Promise} The signed request object; - */ - signRequest(webResource: any): Promise; -} +export type AzExtServiceClientCredentials = AzExtServiceClientCredentialsT2; /** * Loose interface to allow for the use of different versions of "@azure/ms-rest-js" @@ -1295,7 +1279,7 @@ export declare namespace DialogResponses { /** * Call this to register common variables used throughout the UI package. */ -export declare function registerUIExtensionVariables(extVars: UIExtensionVariables): void; +export declare function registerUIExtensionVariables(extVars: UIExtensionVariables): Promise; /** * Call this to create the experimentation service adapter @@ -1307,7 +1291,7 @@ export declare function registerUIExtensionVariables(extVars: UIExtensionVariabl * Public is everyone * NOTE: if unspecified, this will be "Team" if the extension is running in the Development Host, "Insiders" if the extension version contains "alpha", otherwise "Public" */ -export declare function createExperimentationService(ctx: ExtensionContext, targetPopulation?: TargetPopulation): Promise; +//export declare function createExperimentationService(ctx: ExtensionContext, targetPopulation?: TargetPopulation): Promise; /** * Interface for common extension variables used throughout the UI package. diff --git a/utils/package-lock.json b/utils/package-lock.json index 7d604007b6..304352c4f2 100644 --- a/utils/package-lock.json +++ b/utils/package-lock.json @@ -10,13 +10,13 @@ "license": "MIT", "dependencies": { "@vscode/extension-telemetry": "^0.6.2", - "dayjs": "^1.11.2", "escape-string-regexp": "^2.0.0", - "html-to-text": "^8.2.0", - "open": "^8.0.4", "semver": "^7.3.7", + "text-encoding": "^0.7.0", + "uuid": "^9.0.0", "vscode-nls": "^5.0.1", - "vscode-tas-client": "^0.1.47" + "vscode-tas-client": "^0.1.47", + "vscode-uri": "^3.0.6" }, "devDependencies": { "@azure/ms-rest-azure-env": "^2.0.0", @@ -120,6 +120,15 @@ "xml2js": "^0.4.19" } }, + "node_modules/@azure/ms-rest-js/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@azure/ms-rest-nodeauth": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@azure/ms-rest-nodeauth/-/ms-rest-nodeauth-3.1.1.tgz", @@ -494,18 +503,6 @@ "node": ">=10" } }, - "node_modules/@selderee/plugin-htmlparser2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.6.0.tgz", - "integrity": "sha512-J3jpy002TyBjd4N/p6s+s90eX42H2eRhK3SbsZuvTDv977/E8p2U3zikdiehyJja66do7FlxLomZLPlvl2/xaA==", - "dependencies": { - "domhandler": "^4.2.0", - "selderee": "^0.6.0" - }, - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -1870,7 +1867,8 @@ "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true }, "node_modules/commondir": { "version": "1.0.1", @@ -1981,11 +1979,6 @@ "node": ">0.4.0" } }, - "node_modules/dayjs": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.2.tgz", - "integrity": "sha512-F4LXf1OeU9hrSYRPTTj/6FbO4HTjPKXvEIC1P2kcnFurViINCVk3ZV0xAS3XVx9MkMsXbbqlK6hjseaYbgKEHw==" - }, "node_modules/debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", @@ -2030,22 +2023,6 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "engines": { - "node": ">=8" - } - }, "node_modules/define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -2177,11 +2154,6 @@ "node": ">=8" } }, - "node_modules/discontinuous-range": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", - "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==" - }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -2194,57 +2166,6 @@ "node": ">=6.0.0" } }, - "node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, "node_modules/duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -2340,14 +2261,6 @@ "node": ">=8.6" } }, - "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/errno": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", @@ -3575,47 +3488,11 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, "bin": { "he": "bin/he" } }, - "node_modules/html-to-text": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-8.2.0.tgz", - "integrity": "sha512-CLXExYn1b++Lgri+ZyVvbUEFwzkLZppjjZOwB7X1qv2jIi8MrMEvxWX5KQ7zATAzTvcqgmtO00M2kCRMtEdOKQ==", - "dependencies": { - "@selderee/plugin-htmlparser2": "^0.6.0", - "deepmerge": "^4.2.2", - "he": "^1.2.0", - "htmlparser2": "^6.1.0", - "minimist": "^1.2.6", - "selderee": "^0.6.0" - }, - "bin": { - "html-to-text": "bin/cli.js" - }, - "engines": { - "node": ">=10.23.2" - } - }, - "node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, "node_modules/http-proxy-agent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", @@ -3854,20 +3731,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", @@ -4088,17 +3951,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -4512,7 +4364,8 @@ "node_modules/minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true }, "node_modules/minipass": { "version": "3.1.6", @@ -4820,11 +4673,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/moo": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz", - "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==" - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4871,27 +4719,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node_modules/nearley": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", - "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", - "dependencies": { - "commander": "^2.19.0", - "moo": "^0.5.0", - "railroad-diagrams": "^1.0.0", - "randexp": "0.4.6" - }, - "bin": { - "nearley-railroad": "bin/nearley-railroad.js", - "nearley-test": "bin/nearley-test.js", - "nearley-unparse": "bin/nearley-unparse.js", - "nearleyc": "bin/nearleyc.js" - }, - "funding": { - "type": "individual", - "url": "https://nearley.js.org/#give-to-nearley" - } - }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -5113,22 +4940,6 @@ "wrappy": "1" } }, - "node_modules/open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -5206,18 +5017,6 @@ "node": ">=6" } }, - "node_modules/parseley": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.7.0.tgz", - "integrity": "sha512-xyOytsdDu077M3/46Am+2cGXEKM9U9QclBDv7fimY7e+BBlxh2JcBp2mgNsmkyA9uvgyTjVzDi7cP1v4hcFxbw==", - "dependencies": { - "moo": "^0.5.1", - "nearley": "^2.20.1" - }, - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, "node_modules/pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -5415,23 +5214,6 @@ } ] }, - "node_modules/railroad-diagrams": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", - "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==" - }, - "node_modules/randexp": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", - "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", - "dependencies": { - "discontinuous-range": "1.0.0", - "ret": "~0.1.10" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -5547,6 +5329,7 @@ "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, "engines": { "node": ">=0.12" } @@ -5652,17 +5435,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/selderee": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.6.0.tgz", - "integrity": "sha512-ibqWGV5aChDvfVdqNYuaJP/HnVBhlRGSRrlbttmlMpHcLuTqqbMH36QkSs9GEgj5M88JDYLI8eyP94JaQ8xRlg==", - "dependencies": { - "parseley": "^0.7.0" - }, - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, "node_modules/semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -6408,6 +6180,12 @@ "node": ">=0.4.0" } }, + "node_modules/text-encoding": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", + "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", + "deprecated": "no longer maintained" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -7015,10 +6793,9 @@ "dev": true }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", "bin": { "uuid": "dist/bin/uuid" } @@ -7045,6 +6822,11 @@ "vscode": "^1.19.1" } }, + "node_modules/vscode-uri": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.7.tgz", + "integrity": "sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA==" + }, "node_modules/watchpack": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", @@ -7431,6 +7213,14 @@ "tunnel": "0.0.6", "uuid": "^8.3.2", "xml2js": "^0.4.19" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + } } }, "@azure/ms-rest-nodeauth": { @@ -7745,15 +7535,6 @@ } } }, - "@selderee/plugin-htmlparser2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.6.0.tgz", - "integrity": "sha512-J3jpy002TyBjd4N/p6s+s90eX42H2eRhK3SbsZuvTDv977/E8p2U3zikdiehyJja66do7FlxLomZLPlvl2/xaA==", - "requires": { - "domhandler": "^4.2.0", - "selderee": "^0.6.0" - } - }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -8813,7 +8594,8 @@ "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true }, "commondir": { "version": "1.0.1", @@ -8898,11 +8680,6 @@ "integrity": "sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q=", "dev": true }, - "dayjs": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.2.tgz", - "integrity": "sha512-F4LXf1OeU9hrSYRPTTj/6FbO4HTjPKXvEIC1P2kcnFurViINCVk3ZV0xAS3XVx9MkMsXbbqlK6hjseaYbgKEHw==" - }, "debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", @@ -8930,16 +8707,6 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" - }, - "define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -9042,11 +8809,6 @@ "path-type": "^4.0.0" } }, - "discontinuous-range": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", - "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==" - }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -9056,39 +8818,6 @@ "esutils": "^2.0.2" } }, - "dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - } - }, - "domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" - }, - "domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, "duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -9177,11 +8906,6 @@ "ansi-colors": "^4.1.1" } }, - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" - }, "errno": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", @@ -10127,31 +9851,8 @@ "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" - }, - "html-to-text": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-8.2.0.tgz", - "integrity": "sha512-CLXExYn1b++Lgri+ZyVvbUEFwzkLZppjjZOwB7X1qv2jIi8MrMEvxWX5KQ7zATAzTvcqgmtO00M2kCRMtEdOKQ==", - "requires": { - "@selderee/plugin-htmlparser2": "^0.6.0", - "deepmerge": "^4.2.2", - "he": "^1.2.0", - "htmlparser2": "^6.1.0", - "minimist": "^1.2.6", - "selderee": "^0.6.0" - } - }, - "htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true }, "http-proxy-agent": { "version": "4.0.1", @@ -10328,11 +10029,6 @@ "kind-of": "^6.0.2" } }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" - }, "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", @@ -10478,14 +10174,6 @@ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "requires": { - "is-docker": "^2.0.0" - } - }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -10826,7 +10514,8 @@ "minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true }, "minipass": { "version": "3.1.6", @@ -11055,11 +10744,6 @@ "lodash": "^4.17.15" } }, - "moo": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz", - "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==" - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -11097,17 +10781,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "nearley": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", - "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", - "requires": { - "commander": "^2.19.0", - "moo": "^0.5.0", - "railroad-diagrams": "^1.0.0", - "randexp": "0.4.6" - } - }, "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -11271,16 +10944,6 @@ "wrappy": "1" } }, - "open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - } - }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -11337,15 +11000,6 @@ "callsites": "^3.0.0" } }, - "parseley": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.7.0.tgz", - "integrity": "sha512-xyOytsdDu077M3/46Am+2cGXEKM9U9QclBDv7fimY7e+BBlxh2JcBp2mgNsmkyA9uvgyTjVzDi7cP1v4hcFxbw==", - "requires": { - "moo": "^0.5.1", - "nearley": "^2.20.1" - } - }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -11484,20 +11138,6 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, - "railroad-diagrams": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", - "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==" - }, - "randexp": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", - "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", - "requires": { - "discontinuous-range": "1.0.0", - "ret": "~0.1.10" - } - }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -11581,7 +11221,8 @@ "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true }, "reusify": { "version": "1.0.4", @@ -11639,14 +11280,6 @@ "ajv-keywords": "^3.5.2" } }, - "selderee": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.6.0.tgz", - "integrity": "sha512-ibqWGV5aChDvfVdqNYuaJP/HnVBhlRGSRrlbttmlMpHcLuTqqbMH36QkSs9GEgj5M88JDYLI8eyP94JaQ8xRlg==", - "requires": { - "parseley": "^0.7.0" - } - }, "semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -12231,6 +11864,11 @@ } } }, + "text-encoding": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", + "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==" + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -12731,10 +12369,9 @@ "dev": true }, "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" }, "v8-compile-cache": { "version": "2.3.0", @@ -12755,6 +12392,11 @@ "tas-client": "0.1.45" } }, + "vscode-uri": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.7.tgz", + "integrity": "sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA==" + }, "watchpack": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", diff --git a/utils/package.json b/utils/package.json index b49f2f47a9..ef40d9a33c 100644 --- a/utils/package.json +++ b/utils/package.json @@ -33,13 +33,13 @@ }, "dependencies": { "@vscode/extension-telemetry": "^0.6.2", - "dayjs": "^1.11.2", "escape-string-regexp": "^2.0.0", - "html-to-text": "^8.2.0", - "open": "^8.0.4", "semver": "^7.3.7", + "text-encoding": "^0.7.0", + "uuid": "^9.0.0", "vscode-nls": "^5.0.1", - "vscode-tas-client": "^0.1.47" + "vscode-tas-client": "^0.1.47", + "vscode-uri": "^3.0.6" }, "devDependencies": { "@azure/ms-rest-azure-env": "^2.0.0", diff --git a/utils/src/activityLog/Activity.ts b/utils/src/activityLog/Activity.ts index 0711445478..2b913eceee 100644 --- a/utils/src/activityLog/Activity.ts +++ b/utils/src/activityLog/Activity.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { randomUUID } from "crypto"; +import { v4 as uuidv4 } from "uuid"; import { CancellationTokenSource, EventEmitter } from "vscode"; -import * as types from '../../index'; import * as hTypes from '../../hostapi'; +import * as types from '../../index'; import { parseError } from "../parseError"; export enum ActivityStatus { @@ -40,7 +40,7 @@ export abstract class ActivityBase implements hTypes.Activity { abstract errorState(error?: types.IParsedError): hTypes.ActivityTreeItemOptions; public constructor(task: types.ActivityTask) { - this.id = randomUUID(); + this.id = uuidv4(); this.task = task; this.onStart = this._onStartEmitter.event; diff --git a/utils/src/createApiProvider.ts b/utils/src/createApiProvider.ts index 7e5abb70f4..74b3a5f045 100644 --- a/utils/src/createApiProvider.ts +++ b/utils/src/createApiProvider.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as semver from 'semver'; -import { AzureExtensionApiFactory, IActionContext } from '../index'; import { AzureExtensionApi, AzureExtensionApiProvider, GetApiOptions } from '../api'; +import { AzureExtensionApiFactory, IActionContext } from '../index'; import { callWithTelemetryAndErrorHandlingSync } from './callWithTelemetryAndErrorHandling'; import { getPackageInfo } from './getPackageInfo'; import { localize } from './localize'; @@ -14,13 +14,13 @@ function isAzureExtensionApiFactory(maybeAzureExtensionApiFactory: AzureExtensio return (maybeAzureExtensionApiFactory).createApi !== undefined; } -export function createApiProvider(azExts: (AzureExtensionApiFactory | AzureExtensionApi)[]): AzureExtensionApiProvider { +export async function createApiProvider(azExts: (AzureExtensionApiFactory | AzureExtensionApi)[]): Promise { for (const azExt of azExts) { if (!semver.valid(azExt.apiVersion)) { throw new Error(localize('invalidVersion', 'Invalid semver "{0}".', azExt.apiVersion)); } } - const extensionId: string = getPackageInfo().extensionId; + const extensionId: string = (await getPackageInfo()).extensionId; const apiFactories: AzureExtensionApiFactory[] = azExts.map((azExt): AzureExtensionApiFactory => { if (isAzureExtensionApiFactory(azExt)) { diff --git a/utils/src/createExperimentationService.ts b/utils/src/createExperimentationService.ts index 1c4da9ac10..d359d048e9 100644 --- a/utils/src/createExperimentationService.ts +++ b/utils/src/createExperimentationService.ts @@ -1,135 +1,135 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as tas from 'vscode-tas-client'; -import { IActionContext, IExperimentationServiceAdapter } from '../index'; -import { IInternalTelemetryReporter } from './createTelemetryReporter'; -import { ext } from './extensionVariables'; -import { getPackageInfo } from './getPackageInfo'; -import { registerTelemetryHandler } from './index'; - -export async function createExperimentationService(ctx: vscode.ExtensionContext, targetPopulation?: tas.TargetPopulation): Promise { - const result: ExperimentationServiceAdapter = new ExperimentationServiceAdapter(); - const { extensionId, extensionVersion } = getPackageInfo(ctx); - - if (targetPopulation === undefined) { - if (ctx.extensionMode !== vscode.ExtensionMode.Production) { - // Extension is being debugged - targetPopulation = tas.TargetPopulation.Team; - } else if (/alpha/ig.test(extensionVersion)) { - // Extension version has "alpha" - targetPopulation = tas.TargetPopulation.Insiders; - } else if (/Insiders/ig.test(vscode.env.appName)) { - // Running in VSCode Insiders - targetPopulation = tas.TargetPopulation.Insiders; - } else { - targetPopulation = tas.TargetPopulation.Public; - } - } - - try { - result.wrappedExperimentationService = await tas.getExperimentationServiceAsync( - extensionId, - extensionVersion, - targetPopulation, - new ExperimentationTelemetry(ext._internalReporter, ctx), - ctx.globalState - ); - } catch { - // Best effort - } - - return result; -} - -class ExperimentationServiceAdapter implements IExperimentationServiceAdapter { - public wrappedExperimentationService?: tas.IExperimentationService; - - /** - * @deprecated Use `getCachedTreatmentVariable('flight-name') instead - */ - public async isCachedFlightEnabled(flight: string): Promise { - if (!this.wrappedExperimentationService) { - return false; - } - - return !!(await this.getCachedTreatmentVariable(flight)); - } - - /** - * @deprecated Use `getLiveTreatmentVariable('flight-name') instead - */ - public async isLiveFlightEnabled(flight: string): Promise { - if (!this.wrappedExperimentationService) { - return false; - } - - return !!(await this.getLiveTreatmentVariable(flight)); - } - - public async getCachedTreatmentVariable(name: string): Promise { - if (!this.wrappedExperimentationService) { - return Promise.resolve(undefined); - } - - return Promise.resolve(this.wrappedExperimentationService.getTreatmentVariable('vscode', name)); - } - - public async getLiveTreatmentVariable(name: string): Promise { - if (!this.wrappedExperimentationService) { - return undefined; - } - - return this.wrappedExperimentationService.getTreatmentVariableAsync('vscode', name); - } -} - -class ExperimentationTelemetry implements tas.IExperimentationTelemetry { - private readonly sharedProperties: { [key: string]: string } = {}; - - public constructor(private readonly telemetryReporter: IInternalTelemetryReporter, context: vscode.ExtensionContext) { - context.subscriptions.push(registerTelemetryHandler((actionContext: IActionContext) => this.handleTelemetry(actionContext))); - } - - /** - * Implements `postEvent` for `IExperimentationTelemetry`. - * @param eventName The name of the event - * @param props The properties to attach to the event - */ - public postEvent(eventName: string, props: Map): void { - const properties: { [key: string]: string } = {}; - - for (const key of props.keys()) { - properties[key] = props.get(key); - } - - Object.assign(properties, this.sharedProperties); - - // Treat the TAS query event as activation - if (/query-expfeature/i.test(eventName)) { - properties.isActivationEvent = 'true'; - } - - this.telemetryReporter.sendTelemetryErrorEvent(eventName, properties); - } - - /** - * Implements `setSharedProperty` for `IExperimentationTelemetry` - * @param name The name of the property - * @param value The value of the property - */ - public setSharedProperty(name: string, value: string): void { - this.sharedProperties[name] = value; - } - - /** - * Implements a telemetry handler that adds the shared properties to the event - * @param actionContext The action context - */ - public handleTelemetry(actionContext: IActionContext): void { - Object.assign(actionContext.telemetry.properties, this.sharedProperties); - } -} +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See License.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ + +// import * as vscode from 'vscode'; +// import * as tas from 'vscode-tas-client'; +// import { IActionContext, IExperimentationServiceAdapter } from '../index'; +// import { IInternalTelemetryReporter } from './createTelemetryReporter'; +// import { ext } from './extensionVariables'; +// import { getPackageInfo } from './getPackageInfo'; +// import { registerTelemetryHandler } from './index'; + +// export async function createExperimentationService(ctx: vscode.ExtensionContext, targetPopulation?: tas.TargetPopulation): Promise { +// const result: ExperimentationServiceAdapter = new ExperimentationServiceAdapter(); +// const { extensionId, extensionVersion } = getPackageInfo(ctx); + +// if (targetPopulation === undefined) { +// if (ctx.extensionMode !== vscode.ExtensionMode.Production) { +// // Extension is being debugged +// targetPopulation = tas.TargetPopulation.Team; +// } else if (/alpha/ig.test(extensionVersion)) { +// // Extension version has "alpha" +// targetPopulation = tas.TargetPopulation.Insiders; +// } else if (/Insiders/ig.test(vscode.env.appName)) { +// // Running in VSCode Insiders +// targetPopulation = tas.TargetPopulation.Insiders; +// } else { +// targetPopulation = tas.TargetPopulation.Public; +// } +// } + +// try { +// result.wrappedExperimentationService = await tas.getExperimentationServiceAsync( +// extensionId, +// extensionVersion, +// targetPopulation, +// new ExperimentationTelemetry(ext._internalReporter, ctx), +// ctx.globalState +// ); +// } catch { +// // Best effort +// } + +// return result; +// } + +// class ExperimentationServiceAdapter implements IExperimentationServiceAdapter { +// public wrappedExperimentationService?: tas.IExperimentationService; + +// /** +// * @deprecated Use `getCachedTreatmentVariable('flight-name') instead +// */ +// public async isCachedFlightEnabled(flight: string): Promise { +// if (!this.wrappedExperimentationService) { +// return false; +// } + +// return !!(await this.getCachedTreatmentVariable(flight)); +// } + +// /** +// * @deprecated Use `getLiveTreatmentVariable('flight-name') instead +// */ +// public async isLiveFlightEnabled(flight: string): Promise { +// if (!this.wrappedExperimentationService) { +// return false; +// } + +// return !!(await this.getLiveTreatmentVariable(flight)); +// } + +// public async getCachedTreatmentVariable(name: string): Promise { +// if (!this.wrappedExperimentationService) { +// return Promise.resolve(undefined); +// } + +// return Promise.resolve(this.wrappedExperimentationService.getTreatmentVariable('vscode', name)); +// } + +// public async getLiveTreatmentVariable(name: string): Promise { +// if (!this.wrappedExperimentationService) { +// return undefined; +// } + +// return this.wrappedExperimentationService.getTreatmentVariableAsync('vscode', name); +// } +// } + +// class ExperimentationTelemetry implements tas.IExperimentationTelemetry { +// private readonly sharedProperties: { [key: string]: string } = {}; + +// public constructor(private readonly telemetryReporter: IInternalTelemetryReporter, context: vscode.ExtensionContext) { +// context.subscriptions.push(registerTelemetryHandler((actionContext: IActionContext) => this.handleTelemetry(actionContext))); +// } + +// /** +// * Implements `postEvent` for `IExperimentationTelemetry`. +// * @param eventName The name of the event +// * @param props The properties to attach to the event +// */ +// public postEvent(eventName: string, props: Map): void { +// const properties: { [key: string]: string } = {}; + +// for (const key of props.keys()) { +// properties[key] = props.get(key); +// } + +// Object.assign(properties, this.sharedProperties); + +// // Treat the TAS query event as activation +// if (/query-expfeature/i.test(eventName)) { +// properties.isActivationEvent = 'true'; +// } + +// this.telemetryReporter.sendTelemetryErrorEvent(eventName, properties); +// } + +// /** +// * Implements `setSharedProperty` for `IExperimentationTelemetry` +// * @param name The name of the property +// * @param value The value of the property +// */ +// public setSharedProperty(name: string, value: string): void { +// this.sharedProperties[name] = value; +// } + +// /** +// * Implements a telemetry handler that adds the shared properties to the event +// * @param actionContext The action context +// */ +// public handleTelemetry(actionContext: IActionContext): void { +// Object.assign(actionContext.telemetry.properties, this.sharedProperties); +// } +// } diff --git a/utils/src/createTelemetryReporter.ts b/utils/src/createTelemetryReporter.ts index 5ec575e707..b2a562d6ba 100644 --- a/utils/src/createTelemetryReporter.ts +++ b/utils/src/createTelemetryReporter.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import TelemetryReporter from '@vscode/extension-telemetry'; import * as process from 'process'; import * as vscode from 'vscode'; -import TelemetryReporter from '@vscode/extension-telemetry'; import { DebugReporter } from './DebugReporter'; import { getPackageInfo } from './getPackageInfo'; @@ -16,8 +16,8 @@ export interface IInternalTelemetryReporter { sendTelemetryErrorEvent(eventName: string, properties?: { [key: string]: string | undefined }, measurements?: { [key: string]: number | undefined }, errorProps?: string[]): void; } -export function createTelemetryReporter(ctx: vscode.ExtensionContext): IInternalTelemetryReporter { - const { extensionName, extensionVersion, aiKey } = getPackageInfo(ctx); +export async function createTelemetryReporter(ctx: vscode.ExtensionContext): Promise { + const { extensionName, extensionVersion, aiKey } = await getPackageInfo(ctx); let newReporter: IInternalTelemetryReporter; diff --git a/utils/src/extensionUserAgent.ts b/utils/src/extensionUserAgent.ts index dd16c8aab9..af6ec65f0e 100644 --- a/utils/src/extensionUserAgent.ts +++ b/utils/src/extensionUserAgent.ts @@ -13,13 +13,13 @@ export function addExtensionUserAgent(client: IAddUserAgent): void { client.addUserAgentInfo(getExtensionUserAgent()); } -function getExtensionUserAgent(): string { - const { extensionName, extensionVersion } = getPackageInfo(); +async function getExtensionUserAgent(): Promise { + const { extensionName, extensionVersion } = await getPackageInfo(); return `${extensionName}/${extensionVersion}`; } -export function appendExtensionUserAgent(existingUserAgent?: string): string { - const extensionUserAgent: string = getExtensionUserAgent(); +export async function appendExtensionUserAgent(existingUserAgent?: string): Promise { + const extensionUserAgent: string = await getExtensionUserAgent(); existingUserAgent ||= extensionUserAgent; if (existingUserAgent.includes(extensionUserAgent)) { diff --git a/utils/src/extensionVariables.ts b/utils/src/extensionVariables.ts index f1c30d9f40..ffbca781aa 100644 --- a/utils/src/extensionVariables.ts +++ b/utils/src/extensionVariables.ts @@ -36,7 +36,7 @@ class UninitializedExtensionVariables implements types.UIExtensionVariables { */ export let ext: IInternalExtensionVariables = new UninitializedExtensionVariables(); -export function registerUIExtensionVariables(extVars: types.UIExtensionVariables): void { +export async function registerUIExtensionVariables(extVars: types.UIExtensionVariables): Promise { if (ext === extVars) { // already registered return; @@ -45,7 +45,7 @@ export function registerUIExtensionVariables(extVars: types.UIExtensionVariables assert(extVars.context, 'registerUIExtensionVariables: Missing context'); assert(extVars.outputChannel, 'registerUIExtensionVariables: Missing outputChannel'); - ext = Object.assign(extVars, { _internalReporter: createTelemetryReporter(extVars.context) }); + ext = Object.assign(extVars, { _internalReporter: await createTelemetryReporter(extVars.context) }); // eslint-disable-next-line @typescript-eslint/no-misused-promises registerErrorHandler(handleEntryNotFound); diff --git a/utils/src/getPackageInfo.ts b/utils/src/getPackageInfo.ts index 7ab6ffeb46..d29eeeaf4a 100644 --- a/utils/src/getPackageInfo.ts +++ b/utils/src/getPackageInfo.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { ExtensionContext } from "vscode"; +import { Utils } from 'vscode-uri'; import { ext } from "./extensionVariables"; +import { AzExtFsExtra } from "./utils/AzExtFsExtra"; interface IPackageInfo { extensionName: string; @@ -16,13 +18,13 @@ interface IPackageInfo { let packageInfo: IPackageInfo | undefined; -export function getPackageInfo(ctx?: ExtensionContext): IPackageInfo { +export async function getPackageInfo(ctx?: ExtensionContext): Promise { if (!packageInfo) { if (!ctx) { ctx = ext.context; } - const packageJson: IPackageJson = ctx.extension.packageJSON; + const packageJson: IPackageJson = await AzExtFsExtra.readJSON(Utils.joinPath(ctx.extensionUri, 'package.json')) const extensionName: string | undefined = packageJson.name; const extensionVersion: string | undefined = packageJson.version; diff --git a/utils/src/index.ts b/utils/src/index.ts index 51441f7528..f7c28ccc57 100644 --- a/utils/src/index.ts +++ b/utils/src/index.ts @@ -10,7 +10,7 @@ export * from './AzExtResourceType'; export * from './AzExtTreeFileSystem'; export * from './callWithTelemetryAndErrorHandling'; export * from './createApiProvider'; -export { createExperimentationService } from './createExperimentationService'; +//export { createExperimentationService } from './createExperimentationService'; export * from './DialogResponses'; export * from './errors'; export * from './extensionUserAgent'; @@ -35,7 +35,7 @@ export * from './tree/isAzExtTreeItem'; export * from './utils/apiUtils'; export * from './utils/AzExtFsExtra'; export * from './utils/contextUtils'; -export * from './utils/findFreePort'; +//export * from './utils/findFreePort'; export * from './utils/nonNull'; export * from './utils/openUrl'; export * from './wizard/AzureNameStep'; diff --git a/utils/src/parseError.ts b/utils/src/parseError.ts index cff3b4234f..2ab5f2058d 100644 --- a/utils/src/parseError.ts +++ b/utils/src/parseError.ts @@ -5,7 +5,6 @@ /* eslint-disable */ -import * as htmlToText from 'html-to-text'; import { IParsedError } from '../index'; import { localize } from './localize'; import { parseJson } from './utils/parseJson'; @@ -73,7 +72,7 @@ export function parseError(error: any): IParsedError { errorType ||= typeof (error); message ||= localize('unknownError', 'Unknown Error'); - message = parseIfHtml(message); + //message = parseIfHtml(message); // Azure storage SDK errors are presented in XML // https://github.com/Azure/azure-sdk-for-js/issues/6927 @@ -113,33 +112,33 @@ function parseIfJson(o: any): any { return o; } -function parseIfHtml(message: string): string { - if (/(.*)<\/Message>/si); diff --git a/utils/src/pickTreeItem/experiences/compatibility/PickTreeItemWithCompatibility.ts b/utils/src/pickTreeItem/experiences/compatibility/PickTreeItemWithCompatibility.ts index d0a8d69ec9..b2e56f7461 100644 --- a/utils/src/pickTreeItem/experiences/compatibility/PickTreeItemWithCompatibility.ts +++ b/utils/src/pickTreeItem/experiences/compatibility/PickTreeItemWithCompatibility.ts @@ -1,18 +1,18 @@ -import * as types from '../../../../index'; +import { ISubscriptionContext } from '@microsoft/vscode-azext-dev'; import * as vscode from 'vscode'; -import { ResourceGroupsItem } from '../../quickPickAzureResource/tempTypes'; -import { azureResourceExperience } from '../azureResourceExperience'; -import { subscriptionExperience } from '../subscriptionExperience'; +import * as types from '../../../../index'; +import { NoResourceFoundError } from '../../../errors'; +import { isWrapper } from '../../../registerCommandWithTreeNodeUnwrapping'; +import { AzExtTreeItem } from '../../../tree/AzExtTreeItem'; import { isAzExtTreeItem } from '../../../tree/isAzExtTreeItem'; import { createSubscriptionContext } from '../../../utils/credentialUtils'; -import { ISubscriptionContext } from '@microsoft/vscode-azext-dev'; -import { AzExtTreeItem } from '../../../tree/AzExtTreeItem'; -import { CompatibilityRecursiveQuickPickStep } from '../../contextValue/compatibility/CompatibilityRecursiveQuickPickStep'; -import { AzureWizardPromptStep } from '../../../wizard/AzureWizardPromptStep'; import { AzureWizard } from '../../../wizard/AzureWizard'; +import { AzureWizardPromptStep } from '../../../wizard/AzureWizardPromptStep'; +import { CompatibilityRecursiveQuickPickStep } from '../../contextValue/compatibility/CompatibilityRecursiveQuickPickStep'; import { getLastNode } from '../../getLastNode'; -import { NoResourceFoundError } from '../../../errors'; -import { isWrapper } from '../../../registerCommandWithTreeNodeUnwrapping'; +import { ResourceGroupsItem } from '../../quickPickAzureResource/tempTypes'; +import { azureResourceExperience } from '../azureResourceExperience'; +import { subscriptionExperience } from '../subscriptionExperience'; export namespace PickTreeItemWithCompatibility { /** diff --git a/utils/src/registerReportIssueCommand.ts b/utils/src/registerReportIssueCommand.ts index 4f4f82a3ad..834183bbe1 100644 --- a/utils/src/registerReportIssueCommand.ts +++ b/utils/src/registerReportIssueCommand.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as dayjs from 'dayjs'; -// eslint-disable-next-line import/no-internal-modules -import * as relativeTime from 'dayjs/plugin/relativeTime'; +// import * as dayjs from 'dayjs'; +// // eslint-disable-next-line import/no-internal-modules +// import * as relativeTime from 'dayjs/plugin/relativeTime'; import * as types from '../index'; import { localize } from './localize'; import { registerCommand } from "./registerCommand"; import { IReportableIssue, reportAnIssue } from './reportAnIssue'; import { nonNullValue } from './utils/nonNull'; -dayjs.extend(relativeTime); +// dayjs.extend(relativeTime); let cachedIssues: IReportableIssue[] | undefined; export function cacheIssueForCommand(issue: IReportableIssue): void { @@ -45,7 +45,7 @@ export function registerReportIssueCommand(commandId: string): void { return { label: i.error.message, description: i.error.errorType, - detail: `${i.callbackId} - ${dayjs(i.time).fromNow()}`, + //detail: `${i.callbackId} - ${dayjs(i.time).fromNow()}`, data: i }; }); diff --git a/utils/src/reportAnIssue.ts b/utils/src/reportAnIssue.ts index e049675138..d8835bb805 100644 --- a/utils/src/reportAnIssue.ts +++ b/utils/src/reportAnIssue.ts @@ -31,7 +31,7 @@ export async function reportAnIssue(issue: IReportableIssue | undefined): Promis } export async function getReportAnIssueLink(issue: IReportableIssue | undefined): Promise { - const { extensionVersion } = getPackageInfo(); + const { extensionVersion } = await getPackageInfo(); const stack: string = (issue?.error.stack || '').replace(/\r\n/g, '\n'); @@ -70,7 +70,7 @@ Language: ${vscode.env.language}`; body += createBodyDetail(propName, String(value)); } - const simpleLink: string = createNewIssueLinkFromBody(body); + const simpleLink: string = await createNewIssueLinkFromBody(body); if (simpleLink.length <= maxUrlLength) { return simpleLink; } @@ -80,8 +80,8 @@ Language: ${vscode.env.language}`; return createNewIssueLinkFromBody(localize('pasteIssue', "The issue text was copied to the clipboard. Please paste it into this window.")); } -function createNewIssueLinkFromBody(issueBody: string): string { - const { extensionName, bugsUrl } = getPackageInfo(); +async function createNewIssueLinkFromBody(issueBody: string): Promise { + const { extensionName, bugsUrl } = await getPackageInfo(); const baseUrl: string = bugsUrl || `https://github.com/Microsoft/${extensionName}/issues`; return `${baseUrl}/new?body=${encodeURIComponent(issueBody)}`; } diff --git a/utils/src/utils/AzExtFsExtra.ts b/utils/src/utils/AzExtFsExtra.ts index 069bbc3bc7..b763bdea74 100644 --- a/utils/src/utils/AzExtFsExtra.ts +++ b/utils/src/utils/AzExtFsExtra.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as path from 'path'; +import { TextDecoder } from 'text-encoding'; import { FileStat, FileType, Uri, workspace } from 'vscode'; import { parseError } from '../parseError'; @@ -59,7 +60,8 @@ export namespace AzExtFsExtra { export async function readFile(resource: Uri | string): Promise { const uri = convertToUri(resource); - return (await workspace.fs.readFile(uri)).toString(); + const buffer = (await workspace.fs.readFile(uri)); + return new TextDecoder().decode(buffer); } export async function writeFile(resource: Uri | string, contents: string): Promise { @@ -121,6 +123,6 @@ export namespace AzExtFsExtra { } function convertToUri(resource: Uri | string): Uri { - return typeof resource === 'string' ? Uri.file(resource) : resource; + return typeof resource === 'string' ? Uri.parse(resource) : resource; } } diff --git a/utils/src/utils/apiUtils.ts b/utils/src/utils/apiUtils.ts index a004e54241..70c47d3949 100644 --- a/utils/src/utils/apiUtils.ts +++ b/utils/src/utils/apiUtils.ts @@ -20,7 +20,7 @@ export async function getAzureExtensionApi(extensio if (apiProvider) { return apiProvider.getApi(apiVersionRange, { ...options, - extensionId: options?.extensionId ?? getPackageInfo().extensionId + extensionId: options?.extensionId ?? (await getPackageInfo()).extensionId }); } diff --git a/utils/src/utils/credentialUtils.ts b/utils/src/utils/credentialUtils.ts index 4ad847a41b..25acca4db6 100644 --- a/utils/src/utils/credentialUtils.ts +++ b/utils/src/utils/credentialUtils.ts @@ -6,7 +6,6 @@ import * as vscode from 'vscode'; import { AzureSubscription } from '../../hostapi.v2'; import { AzExtServiceClientCredentials, ISubscriptionContext } from '../../index'; -import { localize } from '../localize'; /** * Converts a VS Code authentication session to an Azure Track 1 & 2 compatible compatible credential. @@ -27,9 +26,6 @@ export function createCredential(getSession: (scopes?: string[]) => vscode.Provi } else { return null; } - }, - signRequest: async () => { - throw new Error((localize('signRequestError', 'Track 1 credentials are not (currently) supported.'))); } }; } diff --git a/utils/src/utils/findFreePort.ts b/utils/src/utils/findFreePort.ts index 289c2a6239..de121af4db 100644 --- a/utils/src/utils/findFreePort.ts +++ b/utils/src/utils/findFreePort.ts @@ -1,66 +1,66 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See License.txt in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import * as net from 'net'; -import { randomUtils } from './randomUtils'; +// import * as net from 'net'; +// import { randomUtils } from './randomUtils'; -const DefaultTimeout = 500; // Spend at most 500 ms -const DefaultMaxAttempts = 25; // On at most 25 attempts +// const DefaultTimeout = 500; // Spend at most 500 ms +// const DefaultMaxAttempts = 25; // On at most 25 attempts -const MinRandomPort = 10000; -const MaxRandomPort = 64000; +// const MinRandomPort = 10000; +// const MaxRandomPort = 64000; -/** - * Finds an available port. - * NOTE: If another listener is on '0.0.0.0', this will take the '127.0.0.1' allocation from them! - * @param startPort (Optional) The first port to try. By default, a random port from 10000 (inclusive) to 64000 (exclusive) - * @param maxAttempts (Optional) The maximum number of attempts. 25, by default. - * @param timeout (Optional) The maximum time to spend. 500 ms, by default. - * Adapted from https://github.com/microsoft/vscode/blob/0bf30719729d76dc3db934ac2e04eed892a9ae7e/src/vs/base/node/ports.ts#L150-L191. - * Notable differences: Arguments are optional; if a port attempted is taken, the attempt count is added to it for the next try, this will scan out of a range of taken ports faster - */ -export async function findFreePort(startPort: number = 0, maxAttempts: number = DefaultMaxAttempts, timeout: number = DefaultTimeout): Promise { - // If a start port isn't given, the default is set to 0, and the `||=` will overwrite it with a random value - startPort ||= randomUtils.getRandomInteger(MinRandomPort, MaxRandomPort); +// /** +// * Finds an available port. +// * NOTE: If another listener is on '0.0.0.0', this will take the '127.0.0.1' allocation from them! +// * @param startPort (Optional) The first port to try. By default, a random port from 10000 (inclusive) to 64000 (exclusive) +// * @param maxAttempts (Optional) The maximum number of attempts. 25, by default. +// * @param timeout (Optional) The maximum time to spend. 500 ms, by default. +// * Adapted from https://github.com/microsoft/vscode/blob/0bf30719729d76dc3db934ac2e04eed892a9ae7e/src/vs/base/node/ports.ts#L150-L191. +// * Notable differences: Arguments are optional; if a port attempted is taken, the attempt count is added to it for the next try, this will scan out of a range of taken ports faster +// */ +// export async function findFreePort(startPort: number = 0, maxAttempts: number = DefaultMaxAttempts, timeout: number = DefaultTimeout): Promise { +// // If a start port isn't given, the default is set to 0, and the `||=` will overwrite it with a random value +// startPort ||= randomUtils.getRandomInteger(MinRandomPort, MaxRandomPort); - let resolved: boolean = false; - let timeoutHandle: NodeJS.Timeout | undefined = undefined; - let countTried: number = 1; - const server = net.createServer({ pauseOnConnect: true }); - function doResolve(port: number, resolve: (port: number) => void) { - if (!resolved) { - resolved = true; - server.removeAllListeners(); - server.close(); - if (timeoutHandle) { - clearTimeout(timeoutHandle); - } - resolve(port); - } - } - return new Promise(resolve => { - timeoutHandle = setTimeout(() => { - doResolve(0, resolve); - }, timeout); +// let resolved: boolean = false; +// let timeoutHandle: NodeJS.Timeout | undefined = undefined; +// let countTried: number = 1; +// const server = net.createServer({ pauseOnConnect: true }); +// function doResolve(port: number, resolve: (port: number) => void) { +// if (!resolved) { +// resolved = true; +// server.removeAllListeners(); +// server.close(); +// if (timeoutHandle) { +// clearTimeout(timeoutHandle); +// } +// resolve(port); +// } +// } +// return new Promise(resolve => { +// timeoutHandle = setTimeout(() => { +// doResolve(0, resolve); +// }, timeout); - server.on('listening', () => { - doResolve(startPort, resolve); - }); - server.on('error', err => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any - if (err && ((err).code === 'EADDRINUSE' || (err).code === 'EACCES') && (countTried < maxAttempts)) { - startPort += countTried; - countTried++; - server.listen(startPort, '127.0.0.1'); - } else { - doResolve(0, resolve); - } - }); - server.on('close', () => { - doResolve(0, resolve); - }); - server.listen(startPort, '127.0.0.1'); - }); -} +// server.on('listening', () => { +// doResolve(startPort, resolve); +// }); +// server.on('error', err => { +// // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any +// if (err && ((err).code === 'EADDRINUSE' || (err).code === 'EACCES') && (countTried < maxAttempts)) { +// startPort += countTried; +// countTried++; +// server.listen(startPort, '127.0.0.1'); +// } else { +// doResolve(0, resolve); +// } +// }); +// server.on('close', () => { +// doResolve(0, resolve); +// }); +// server.listen(startPort, '127.0.0.1'); +// }); +// } diff --git a/utils/src/utils/nonNull.ts b/utils/src/utils/nonNull.ts index 256c8330d9..9604eebd82 100644 --- a/utils/src/utils/nonNull.ts +++ b/utils/src/utils/nonNull.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isNullOrUndefined } from 'util'; - /** * Retrieves a property by name from an object and checks that it's not null and not undefined. It is strongly typed * for the property and will give a compile error if the given name is not a property of the source. @@ -48,3 +46,7 @@ export function nonNullOrEmptyValue(value: string | undefined, propertyNameOrMes export function nonNullValueAndProp(source: TSource | undefined, name: TKey): NonNullable { return nonNullProp(nonNullValue(source, name), name); } + +function isNullOrUndefined(value: unknown): value is null | undefined { + return value === null || value === undefined; +} diff --git a/utils/src/utils/openUrl.ts b/utils/src/utils/openUrl.ts index ddc7381ab2..20586f1352 100644 --- a/utils/src/utils/openUrl.ts +++ b/utils/src/utils/openUrl.ts @@ -3,11 +3,9 @@ * Licensed under the MIT License. See License.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import open = require("open"); +import * as vscode from 'vscode'; export async function openUrl(url: string): Promise { // Using this functionality is blocked by https://github.com/Microsoft/vscode/issues/85930 - // await vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(url)); - - await open(url); + await vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(url)); } diff --git a/utils/test/apiProvider.test.ts b/utils/test/apiProvider.test.ts index e80b77f537..8b58bfbdaf 100644 --- a/utils/test/apiProvider.test.ts +++ b/utils/test/apiProvider.test.ts @@ -55,7 +55,7 @@ suite('AzureExtensionApiProvider tests', () => { const api11: TestApiFactory = new TestApiFactory('1.1.0'); const api111: TestApiFactory = new TestApiFactory('1.1.1'); const api12: TestApiFactory = new TestApiFactory('1.2.0'); - const apiProvider: AzureExtensionApiProvider = createApiProvider([api1, api111, api11, api12]); + const apiProvider: AzureExtensionApiProvider = await createApiProvider([api1, api111, api11, api12]); /* eslint-disable @typescript-eslint/no-unsafe-return */ assert.throws(() => apiProvider.getApi('0.1'), (error) => validateApiError(error, /no longer supported/, 'NoLongerSupported')); @@ -75,14 +75,14 @@ suite('AzureExtensionApiProvider tests', () => { const latestApi11Tilde: TestApi = apiProvider.getApi('~1.1.0'); assert.equal(latestApi11Tilde.apiVersion, '1.1.1'); - const emptyApiProvider: AzureExtensionApiProvider = createApiProvider([]); + const emptyApiProvider: AzureExtensionApiProvider = await createApiProvider([]); // eslint-disable-next-line @typescript-eslint/no-unsafe-return assert.throws(() => emptyApiProvider.getApi('^1.0.0'), (error) => validateApiError(error, /must be updated/, 'NotYetSupported')); }); test('Wrapped api is same as original api', async () => { const apiFactory: TestApiFactory = new TestApiFactory('1.0.0'); - const apiProvider: AzureExtensionApiProvider = createApiProvider([apiFactory]); + const apiProvider: AzureExtensionApiProvider = await createApiProvider([apiFactory]); const api = apiFactory.createApi(); const wrappedApi: TestApi = apiProvider.getApi('1');