From 5089ef7366c4fc6e91a229d0221179dc07da5b3f Mon Sep 17 00:00:00 2001 From: Megan Mott <59709511+motm32@users.noreply.github.com> Date: Fri, 31 Jan 2025 11:02:14 -0800 Subject: [PATCH] Add local settings node under local project in workspace view (#4367) * add local settings node * change * update appsettings package --- package-lock.json | 8 +-- package.json | 2 +- .../localSettings/LocalSettingsClient.ts | 64 +++++++++++++++++++ .../localSettings/getLocalSettingsFile.ts | 17 ++++- src/tree/localProject/LocalProjectTreeItem.ts | 9 ++- 5 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 src/commands/appSettings/localSettings/LocalSettingsClient.ts diff --git a/package-lock.json b/package-lock.json index 2b835dd53..0f6143aa9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "@azure/core-rest-pipeline": "^1.11.0", "@azure/storage-blob": "^12.5.0", "@microsoft/vscode-azext-azureappservice": "^3.3.1", - "@microsoft/vscode-azext-azureappsettings": "^0.2.1", + "@microsoft/vscode-azext-azureappsettings": "^0.2.2", "@microsoft/vscode-azext-azureutils": "^3.1.3", "@microsoft/vscode-azext-serviceconnector": "^0.1.3", "@microsoft/vscode-azext-utils": "^2.5.7", @@ -1140,9 +1140,9 @@ } }, "node_modules/@microsoft/vscode-azext-azureappsettings": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-azureappsettings/-/vscode-azext-azureappsettings-0.2.1.tgz", - "integrity": "sha512-YfYnXC/Gmx86+U+lAwui0EUlzRtOxSSrPcSmYsJHR7iVZ1Xzr7nblQveNTHKOUHaphSoNGnz9+2mzcYQwJnpNQ==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-azureappsettings/-/vscode-azext-azureappsettings-0.2.2.tgz", + "integrity": "sha512-qZr7jtP9628k7KeltK2FQP5wEK/oVr3eR26X9Yts3QIzHOsMpbpGATyaczBcXq6w+Qro8A0SAIGg6v+c4QcVTQ==", "dependencies": { "@microsoft/vscode-azext-utils": "^2.0.0" } diff --git a/package.json b/package.json index 4c3d53630..b86c6e66a 100644 --- a/package.json +++ b/package.json @@ -1239,7 +1239,7 @@ "@azure/core-rest-pipeline": "^1.11.0", "@azure/storage-blob": "^12.5.0", "@microsoft/vscode-azext-azureappservice": "^3.3.1", - "@microsoft/vscode-azext-azureappsettings": "^0.2.1", + "@microsoft/vscode-azext-azureappsettings": "^0.2.2", "@microsoft/vscode-azext-azureutils": "^3.1.3", "@microsoft/vscode-azext-serviceconnector": "^0.1.3", "@microsoft/vscode-azext-utils": "^2.5.7", diff --git a/src/commands/appSettings/localSettings/LocalSettingsClient.ts b/src/commands/appSettings/localSettings/LocalSettingsClient.ts new file mode 100644 index 000000000..c8933fb55 --- /dev/null +++ b/src/commands/appSettings/localSettings/LocalSettingsClient.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.md in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { type StringDictionary } from "@azure/arm-appservice"; +import { type AppSettingsClientProvider, type IAppSettingsClient } from "@microsoft/vscode-azext-azureappsettings"; +import { AzExtFsExtra, callWithTelemetryAndErrorHandling, type IActionContext } from "@microsoft/vscode-azext-utils"; +import * as vscode from 'vscode'; +import { type ILocalSettingsJson } from "../../../funcConfig/local.settings"; +import { type LocalProjectTreeItem } from "../../../tree/localProject/LocalProjectTreeItem"; +import { decryptLocalSettings } from "./decryptLocalSettings"; +import { encryptLocalSettings } from "./encryptLocalSettings"; +import { getLocalSettingsFileNoPrompt } from "./getLocalSettingsFile"; + +export class LocalSettingsClientProvider implements AppSettingsClientProvider { + private _node: LocalProjectTreeItem; + constructor(node: LocalProjectTreeItem) { + this._node = node; + } + + public async createClient(): Promise { + return new LocalSettingsClient(this._node); + } +} + +export class LocalSettingsClient implements IAppSettingsClient { + public fullName: string; + public isLinux: boolean; + private _node: LocalProjectTreeItem; + + constructor(node: LocalProjectTreeItem) { + this.isLinux = false; + this.fullName = 'local'; + this._node = node; + } + + public async listApplicationSettings(): Promise { + const result = await callWithTelemetryAndErrorHandling('listApplicationSettings', async (context: IActionContext) => { + const localSettingsPath: string | undefined = await getLocalSettingsFileNoPrompt(context, this._node.workspaceFolder); + if (localSettingsPath === undefined) { + return { properties: {} }; + } else { + const localSettingsUri: vscode.Uri = vscode.Uri.file(localSettingsPath); + + let localSettings: ILocalSettingsJson = await AzExtFsExtra.readJSON(localSettingsPath); + if (localSettings.IsEncrypted) { + await decryptLocalSettings(context, localSettingsUri); + try { + localSettings = await AzExtFsExtra.readJSON(localSettingsPath); + } finally { + await encryptLocalSettings(context, localSettingsUri); + } + } + return { properties: localSettings.Values }; + } + }); + return result ?? { properties: {} }; + } + + public async updateApplicationSettings(): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/src/commands/appSettings/localSettings/getLocalSettingsFile.ts b/src/commands/appSettings/localSettings/getLocalSettingsFile.ts index 99839c937..3b49eca7f 100644 --- a/src/commands/appSettings/localSettings/getLocalSettingsFile.ts +++ b/src/commands/appSettings/localSettings/getLocalSettingsFile.ts @@ -5,7 +5,7 @@ import { AzExtFsExtra, type IActionContext } from '@microsoft/vscode-azext-utils'; import * as path from 'path'; -import { type WorkspaceFolder } from "vscode"; +import type * as vscode from 'vscode'; import { localSettingsFileName } from '../../../constants'; import { getRootWorkspaceFolder, selectWorkspaceFile } from '../../../utils/workspace'; import { tryGetFunctionProjectRoot } from '../../createNewProject/verifyIsProject'; @@ -14,7 +14,7 @@ import { tryGetFunctionProjectRoot } from '../../createNewProject/verifyIsProjec * If only one project is open and the default local settings file exists, return that. * Otherwise, prompt */ -export async function getLocalSettingsFile(context: IActionContext, message: string, workspaceFolder?: WorkspaceFolder): Promise { +export async function getLocalSettingsFile(context: IActionContext, message: string, workspaceFolder?: vscode.WorkspaceFolder): Promise { workspaceFolder ||= await getRootWorkspaceFolder(); if (workspaceFolder) { const projectPath: string | undefined = await tryGetFunctionProjectRoot(context, workspaceFolder); @@ -26,8 +26,19 @@ export async function getLocalSettingsFile(context: IActionContext, message: str } } - return await selectWorkspaceFile(context, message, async (f: WorkspaceFolder): Promise => { + return await selectWorkspaceFile(context, message, async (f: vscode.WorkspaceFolder): Promise => { const projectPath: string = await tryGetFunctionProjectRoot(context, f) || f.uri.fsPath; return path.relative(f.uri.fsPath, path.join(projectPath, localSettingsFileName)); }); } + +export async function getLocalSettingsFileNoPrompt(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder): Promise { + const projectPath: string | undefined = await tryGetFunctionProjectRoot(context, workspaceFolder); + if (projectPath) { + const localSettingsFile: string = path.join(projectPath, localSettingsFileName); + if (await AzExtFsExtra.pathExists(localSettingsFile)) { + return localSettingsFile; + } + } + return undefined; +} diff --git a/src/tree/localProject/LocalProjectTreeItem.ts b/src/tree/localProject/LocalProjectTreeItem.ts index a23a83b12..ac2799974 100644 --- a/src/tree/localProject/LocalProjectTreeItem.ts +++ b/src/tree/localProject/LocalProjectTreeItem.ts @@ -3,12 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { AppSettingsTreeItem } from '@microsoft/vscode-azext-azureappsettings'; import { callWithTelemetryAndErrorHandling, type AzExtParentTreeItem, type AzExtTreeItem, type IActionContext } from '@microsoft/vscode-azext-utils'; import * as path from 'path'; import { Disposable, type TaskScope, type WorkspaceFolder } from 'vscode'; import { type FuncVersion } from '../../FuncVersion'; +import { LocalSettingsClientProvider } from '../../commands/appSettings/localSettings/LocalSettingsClient'; import { onDotnetFuncTaskReady } from '../../commands/pickFuncProcess'; import { functionJsonFileName, localSettingsFileName, type ProjectLanguage } from '../../constants'; +import { ext } from '../../extensionVariables'; import { type IParsedHostJson } from '../../funcConfig/host'; import { onFuncTaskStarted } from '../../funcCoreTools/funcHostTask'; import { type LocalProjectInternal } from '../../workspace/listLocalProjects'; @@ -35,6 +38,7 @@ export class LocalProjectTreeItem extends LocalProjectTreeItemBase implements Di private readonly _disposables: Disposable[] = []; private readonly _localFunctionsTreeItem: LocalFunctionsTreeItem; + private readonly _localSettingsTreeItem: AppSettingsTreeItem; public constructor(parent: AzExtParentTreeItem, localProject: LocalProjectInternal) { const options = localProject.options; @@ -56,6 +60,9 @@ export class LocalProjectTreeItem extends LocalProjectTreeItemBase implements Di this._disposables.push(onDotnetFuncTaskReady(async scope => this.onFuncTaskChanged(scope))); this._localFunctionsTreeItem = new LocalFunctionsTreeItem(this); + this._localSettingsTreeItem = new AppSettingsTreeItem(this, new LocalSettingsClientProvider(this), ext.prefix, { + contextValuesToAdd: ['localSettings'] + }); } public async getHostRequest(context: IActionContext): Promise { @@ -72,7 +79,7 @@ export class LocalProjectTreeItem extends LocalProjectTreeItemBase implements Di // eslint-disable-next-line @typescript-eslint/require-await public async loadMoreChildrenImpl(_clearCache: boolean): Promise { - return [this._localFunctionsTreeItem]; + return [this._localFunctionsTreeItem, this._localSettingsTreeItem]; } public isAncestorOfImpl(contextValue: string | RegExp): boolean {