diff --git a/package-lock.json b/package-lock.json index e395ab0dc..02745cc92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@microsoft/vscode-azext-azureappsettings": "^0.2.11", "@microsoft/vscode-azext-azureutils": "^3.5.2", "@microsoft/vscode-azext-utils": "^3.5.0", - "@microsoft/vscode-azureresources-api": "^2.6.2", + "@microsoft/vscode-azureresources-api": "file:../vscode-azureresourcegroups/api/microsoft-vscode-azureresources-api-3.0.0.tgz", "@microsoft/vscode-container-client": "^0.4.3", "cross-fetch": "^4.0.0", "escape-string-regexp": "^4.0.0", @@ -1434,6 +1434,15 @@ "@azure/ms-rest-azure-env": "^2.0.0" } }, + "node_modules/@microsoft/vscode-azext-utils/node_modules/@microsoft/vscode-azureresources-api": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@microsoft/vscode-azureresources-api/-/vscode-azureresources-api-2.6.3.tgz", + "integrity": "sha512-uwFHLc9fsbuBPKC/WOU+p5JMj9VyNyU1k+3T1uFp00l4OMmazqBqiJYKao6jc/d525hy9FW6EzniGPHdocKApA==", + "license": "MIT", + "peerDependencies": { + "@azure/ms-rest-azure-env": "^2.0.0" + } + }, "node_modules/@microsoft/vscode-azext-utils/node_modules/escape-string-regexp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", @@ -1443,9 +1452,13 @@ } }, "node_modules/@microsoft/vscode-azureresources-api": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@microsoft/vscode-azureresources-api/-/vscode-azureresources-api-2.6.2.tgz", - "integrity": "sha512-VJdBdcwrecL3M3qZ8R2Te+RfgI63Xk4a/HX/rzq/xVx5QqbjdyuwJk6vf76frFOskmpET5/o5PiqVkTZs0wWeA==", + "version": "3.0.0", + "resolved": "file:../vscode-azureresourcegroups/api/microsoft-vscode-azureresources-api-3.0.0.tgz", + "integrity": "sha512-ARnUynl0EpXa7yeKv+pZONTWFOTyDGqGbOF8prhlPy0RDsQ3iTg5XvlN7gVitdlh8wwYMlm5OviD7YbYcFlAyQ==", + "license": "MIT", + "engines": { + "vscode": "^1.105.0" + }, "peerDependencies": { "@azure/ms-rest-azure-env": "^2.0.0" } diff --git a/package.json b/package.json index ffa8b5fcd..5e6414e69 100644 --- a/package.json +++ b/package.json @@ -1478,7 +1478,7 @@ "@microsoft/vscode-azext-azureappsettings": "^0.2.11", "@microsoft/vscode-azext-azureutils": "^3.5.2", "@microsoft/vscode-azext-utils": "^3.5.0", - "@microsoft/vscode-azureresources-api": "^2.6.2", + "@microsoft/vscode-azureresources-api": "file:../vscode-azureresourcegroups/api/microsoft-vscode-azureresources-api-3.0.0.tgz", "@microsoft/vscode-container-client": "^0.4.3", "cross-fetch": "^4.0.0", "escape-string-regexp": "^4.0.0", diff --git a/src/commands/api/createAzureFunctionsApiProvider.ts b/src/commands/api/createAzureFunctionsApiProvider.ts new file mode 100644 index 000000000..36e91a759 --- /dev/null +++ b/src/commands/api/createAzureFunctionsApiProvider.ts @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.md in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { callWithTelemetryAndErrorHandling, createApiProvider, maskUserInfo, type apiUtils, type IActionContext } from "@microsoft/vscode-azext-utils"; +import { type AzureHostExtensionApi } from "@microsoft/vscode-azext-utils/hostapi"; +import { AzExtResourceType, prepareAzureResourcesApiRequest, type AzureExtensionApi, type AzureResourcesApiRequestContext, type AzureResourcesApiRequestError, type AzureResourcesExtensionApi } from "@microsoft/vscode-azureresources-api"; +import { ext } from "../../extensionVariables"; +import { validateFuncCoreToolsInstalled } from "../../funcCoreTools/validateFuncCoreToolsInstalled"; +import { FunctionAppResolver } from "../../FunctionAppResolver"; +import { localize } from "../../localize"; +import { FunctionsLocalResourceProvider } from "../../LocalResourceProvider"; +import { type DurableTaskSchedulerClient } from "../../tree/durableTaskScheduler/DurableTaskSchedulerClient"; +import { type DurableTaskSchedulerDataBranchProvider } from "../../tree/durableTaskScheduler/DurableTaskSchedulerDataBranchProvider"; +import { type DurableTaskSchedulerEmulatorClient } from "../../tree/durableTaskScheduler/DurableTaskSchedulerEmulatorClient"; +import { DurableTaskSchedulerWorkspaceDataBranchProvider } from "../../tree/durableTaskScheduler/DurableTaskSchedulerWorkspaceDataBranchProvider"; +import { DurableTaskSchedulerWorkspaceResourceProvider } from "../../tree/durableTaskScheduler/DurableTaskSchedulerWorkspaceResourceProvider"; +import { type AzureFunctionsExtensionApi } from "../../vscode-azurefunctions.api"; +import { listLocalFunctions } from "../../workspace/listLocalFunctions"; +import { listLocalProjects } from "../../workspace/listLocalProjects"; +import { startFuncProcessFromApi } from "../pickFuncProcess"; +import { createFunctionFromApi } from "./createFunctionFromApi"; +import { downloadAppSettingsFromApi } from "./downloadAppSettingsFromApi"; +import { revealTreeItem } from "./revealTreeItem"; +import { uploadAppSettingsFromApi } from "./uploadAppSettingsFromApi"; + +export function createAzureFunctionsApiProvider( + services: { + dts: { + dataBranchProvider: DurableTaskSchedulerDataBranchProvider, + emulatorClient: DurableTaskSchedulerEmulatorClient, + schedulerClient: DurableTaskSchedulerClient, + }, + }, +): apiUtils.AzureExtensionApiProvider { + + const v1: string = '0.0.1'; + const v2: string = '^2.0.0'; + + const context: AzureResourcesApiRequestContext = { + azureResourcesApiVersions: [v1, v2], + clientExtensionId: ext.context.extension.id, + + onDidReceiveAzureResourcesApis: async (azureResourcesApis: (AzureExtensionApi | AzureResourcesExtensionApi | undefined)[]) => { + await callWithTelemetryAndErrorHandling('azureFunctions.hostApiRequestSucceeded', (actionContext: IActionContext) => { + actionContext.errorHandling.rethrow = true; + + const [rgApi, rgApiV2] = azureResourcesApis; + if (!rgApi) { + throw new Error(createNoMatchingVersionMessage(v1)); + } + if (!rgApiV2) { + throw new Error(createNoMatchingVersionMessage(v2)); + } + + ext.rgApi = rgApi as AzureHostExtensionApi; + ext.rgApi.registerApplicationResourceResolver(AzExtResourceType.FunctionApp, new FunctionAppResolver()); + ext.rgApi.registerWorkspaceResourceProvider('func', new FunctionsLocalResourceProvider()); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + ext.azureAccountTreeItem = ext.rgApi.appResourceTree._rootTreeItem as AzureAccountTreeItemBase; + + ext.rgApiV2 = rgApiV2 as AzureResourcesExtensionApi; + ext.rgApiV2.resources.registerAzureResourceBranchDataProvider('DurableTaskScheduler' as AzExtResourceType, services.dts.dataBranchProvider); + ext.rgApiV2.resources.registerWorkspaceResourceProvider(new DurableTaskSchedulerWorkspaceResourceProvider()); + ext.rgApiV2.resources.registerWorkspaceResourceBranchDataProvider( + 'DurableTaskSchedulerEmulator', + new DurableTaskSchedulerWorkspaceDataBranchProvider(services.dts.emulatorClient)); + }); + }, + + onApiRequestError: async (error: AzureResourcesApiRequestError) => { + await callWithTelemetryAndErrorHandling('azureFunctions.hostApiRequestFailed', (actionContext: IActionContext) => { + actionContext.telemetry.properties.hostApiRequestErrorCode = error.code; + actionContext.telemetry.properties.hostApiRequestError = maskUserInfo(error.message, []); + }); + throw error; + }, + + }; + + const functionAppsApi: AzureFunctionsExtensionApi = { + apiVersion: '1.10.0', + revealTreeItem, + createFunction: createFunctionFromApi, + downloadAppSettings: downloadAppSettingsFromApi, + uploadAppSettings: uploadAppSettingsFromApi, + listLocalProjects: listLocalProjects, + listLocalFunctions: listLocalFunctions, + isFuncCoreToolsInstalled: async (message: string) => { + return !!await callWithTelemetryAndErrorHandling('azureFunctions.api.isFuncCoreToolsInstalled', async (actionContext: IActionContext) => { + return await validateFuncCoreToolsInstalled(actionContext, message, undefined); + }); + }, + startFuncProcess: startFuncProcessFromApi, + }; + + const { clientApi, requestResourcesApis } = prepareAzureResourcesApiRequest(context, functionAppsApi); + requestResourcesApis(); + return createApiProvider([clientApi]); +} + +function createNoMatchingVersionMessage(apiVersion: string): string { + return localize('noMatchingApi', 'Failed to find a matching Azure Resources API for version "{0}".', apiVersion); +} diff --git a/src/extension.ts b/src/extension.ts index 47be7118f..2edbb2738 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,18 +6,11 @@ 'use strict'; import { registerAppServiceExtensionVariables } from '@microsoft/vscode-azext-azureappservice'; -import { registerAzureUtilsExtensionVariables, type AzureAccountTreeItemBase } from '@microsoft/vscode-azext-azureutils'; +import { registerAzureUtilsExtensionVariables } from '@microsoft/vscode-azext-azureutils'; import { callWithTelemetryAndErrorHandling, createApiProvider, createAzExtOutputChannel, createExperimentationService, registerErrorHandler, registerEvent, registerReportIssueCommand, registerUIExtensionVariables, type IActionContext, type apiUtils } from '@microsoft/vscode-azext-utils'; -import { AzExtResourceType, getAzureResourcesExtensionApi } from '@microsoft/vscode-azureresources-api'; import * as vscode from 'vscode'; -import { FunctionAppResolver } from './FunctionAppResolver'; -import { FunctionsLocalResourceProvider } from './LocalResourceProvider'; -import { createFunctionFromApi } from './commands/api/createFunctionFromApi'; -import { downloadAppSettingsFromApi } from './commands/api/downloadAppSettingsFromApi'; -import { revealTreeItem } from './commands/api/revealTreeItem'; -import { uploadAppSettingsFromApi } from './commands/api/uploadAppSettingsFromApi'; +import { createAzureFunctionsApiProvider } from './commands/api/createAzureFunctionsApiProvider'; import { runPostFunctionCreateStepsFromCache } from './commands/createFunction/FunctionCreateStepBase'; -import { startFuncProcessFromApi } from './commands/pickFuncProcess'; import { registerCommands } from './commands/registerCommands'; import { func } from './constants'; import { BallerinaDebugProvider } from './debug/BallerinaDebugProvider'; @@ -29,21 +22,14 @@ import { PythonDebugProvider } from './debug/PythonDebugProvider'; import { handleUri } from './downloadAzureProject/handleUri'; import { ext } from './extensionVariables'; import { registerFuncHostTaskEvents } from './funcCoreTools/funcHostTask'; -import { validateFuncCoreToolsInstalled } from './funcCoreTools/validateFuncCoreToolsInstalled'; import { validateFuncCoreToolsIsLatest } from './funcCoreTools/validateFuncCoreToolsIsLatest'; -import { getResourceGroupsApi } from './getExtensionApi'; import { CentralTemplateProvider } from './templates/CentralTemplateProvider'; import { ShellContainerClient } from './tree/durableTaskScheduler/ContainerClient'; import { HttpDurableTaskSchedulerClient } from './tree/durableTaskScheduler/DurableTaskSchedulerClient'; import { DurableTaskSchedulerDataBranchProvider } from './tree/durableTaskScheduler/DurableTaskSchedulerDataBranchProvider'; import { DockerDurableTaskSchedulerEmulatorClient } from './tree/durableTaskScheduler/DurableTaskSchedulerEmulatorClient'; -import { DurableTaskSchedulerWorkspaceDataBranchProvider } from './tree/durableTaskScheduler/DurableTaskSchedulerWorkspaceDataBranchProvider'; -import { DurableTaskSchedulerWorkspaceResourceProvider } from './tree/durableTaskScheduler/DurableTaskSchedulerWorkspaceResourceProvider'; import { registerContentProvider } from './utils/textUtils'; import { verifyVSCodeConfigOnActivate } from './vsCodeConfig/verifyVSCodeConfigOnActivate'; -import { type AzureFunctionsExtensionApi } from './vscode-azurefunctions.api'; -import { listLocalFunctions } from './workspace/listLocalFunctions'; -import { listLocalProjects } from './workspace/listLocalProjects'; const emulatorClient = new DockerDurableTaskSchedulerEmulatorClient(new ShellContainerClient()); @@ -57,7 +43,8 @@ export async function activateInternal(context: vscode.ExtensionContext, perfSta registerAzureUtilsExtensionVariables(ext); registerAppServiceExtensionVariables(ext); - await callWithTelemetryAndErrorHandling('azureFunctions.activate', async (activateContext: IActionContext) => { + return await callWithTelemetryAndErrorHandling('azureFunctions.activate', async (activateContext: IActionContext) => { + activateContext.errorHandling.rethrow = true; activateContext.telemetry.properties.isActivationEvent = 'true'; activateContext.telemetry.measurements.mainFileLoad = (perfStats.loadEndTime - perfStats.loadStartTime) / 1000; @@ -84,13 +71,12 @@ export async function activateInternal(context: vscode.ExtensionContext, perfSta const schedulerClient = new HttpDurableTaskSchedulerClient(); const dataBranchProvider = new DurableTaskSchedulerDataBranchProvider(schedulerClient); - registerCommands({ - dts: { - dataBranchProvider, - emulatorClient, - schedulerClient - } - }); + const dts = { + dataBranchProvider, + emulatorClient, + schedulerClient + }; + registerCommands({ dts }); registerFuncHostTaskEvents(); @@ -114,42 +100,11 @@ export async function activateInternal(context: vscode.ExtensionContext, perfSta })); registerContentProvider(); - ext.experimentationService = await createExperimentationService(context); - ext.rgApi = await getResourceGroupsApi(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - ext.azureAccountTreeItem = ext.rgApi.appResourceTree._rootTreeItem as AzureAccountTreeItemBase; - ext.rgApi.registerApplicationResourceResolver(AzExtResourceType.FunctionApp, new FunctionAppResolver()); - ext.rgApi.registerWorkspaceResourceProvider('func', new FunctionsLocalResourceProvider()); - - const azureResourcesApi = await getAzureResourcesExtensionApi(context, '2.0.0'); - - ext.rgApiV2 = azureResourcesApi; - - azureResourcesApi.resources.registerAzureResourceBranchDataProvider('DurableTaskScheduler' as AzExtResourceType, dataBranchProvider); - - azureResourcesApi.resources.registerWorkspaceResourceProvider(new DurableTaskSchedulerWorkspaceResourceProvider()); - azureResourcesApi.resources.registerWorkspaceResourceBranchDataProvider( - 'DurableTaskSchedulerEmulator', - new DurableTaskSchedulerWorkspaceDataBranchProvider(emulatorClient)); - }); - - return createApiProvider([{ - revealTreeItem, - createFunction: createFunctionFromApi, - downloadAppSettings: downloadAppSettingsFromApi, - uploadAppSettings: uploadAppSettingsFromApi, - listLocalProjects: listLocalProjects, - listLocalFunctions: listLocalFunctions, - isFuncCoreToolsInstalled: async (message: string) => { - return await callWithTelemetryAndErrorHandling('azureFunctions.api.isFuncCoreToolsInstalled', async (context: IActionContext) => { - return await validateFuncCoreToolsInstalled(context, message, undefined); - }); - }, - startFuncProcess: startFuncProcessFromApi, - apiVersion: '1.10.0' - }]); + + return createAzureFunctionsApiProvider({ dts }); + + }) ?? createApiProvider([]); } export async function deactivateInternal(): Promise { diff --git a/src/extensionVariables.ts b/src/extensionVariables.ts index 51c6094ad..dae26b316 100644 --- a/src/extensionVariables.ts +++ b/src/extensionVariables.ts @@ -5,6 +5,7 @@ import { type IActionContext, type IAzExtOutputChannel, type IExperimentationServiceAdapter } from '@microsoft/vscode-azext-utils'; import { type AzureHostExtensionApi } from '@microsoft/vscode-azext-utils/hostapi'; +import { type AzureResourcesExtensionApi } from '@microsoft/vscode-azureresources-api'; import { type ExtensionContext } from 'vscode'; import { type EventGridCodeLensProvider } from './commands/executeFunction/eventGrid/EventGridCodeLensProvider'; import { func } from './constants'; @@ -12,7 +13,6 @@ import { type CentralTemplateProvider } from './templates/CentralTemplateProvide import { type AzureAccountTreeItemWithProjects } from './tree/AzureAccountTreeItemWithProjects'; import { type FunctionTreeItemBase } from './tree/FunctionTreeItemBase'; import { type IFunction } from './workspace/LocalFunction'; -import { type AzureResourcesExtensionApi } from '@microsoft/vscode-azureresources-api'; /** * Used for extensionVariables that can also be set per-action diff --git a/src/vscode-azurefunctions.api.d.ts b/src/vscode-azurefunctions.api.d.ts index f34e2e80f..fa092495f 100644 --- a/src/vscode-azurefunctions.api.d.ts +++ b/src/vscode-azurefunctions.api.d.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { type AzureWizardExecuteStep, type IActionContext } from "@microsoft/vscode-azext-utils"; +import { type AzureExtensionApi } from "@microsoft/vscode-azureresources-api"; import type * as vscode from 'vscode'; export interface ILocalFunction { @@ -55,7 +56,7 @@ interface ListLocalProjectsResult { invalidProjects: InvalidLocalProject[]; } -export interface AzureFunctionsExtensionApi { +export interface AzureFunctionsExtensionApi extends AzureExtensionApi { apiVersion: string; revealTreeItem(resourceId: string): Promise;