Skip to content

Commit

Permalink
fix(vscode): Fix designtime local.settings overwriting (#4983)
Browse files Browse the repository at this point in the history
* Add function to get local.settings schema

* Add test for local settings schema

* Redirect import to node_modules
  • Loading branch information
ccastrotrejo committed Jun 14, 2024
1 parent 8cf13cd commit b010740
Show file tree
Hide file tree
Showing 12 changed files with 161 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { extensionCommand } from '../../../constants';
import type { Uri } from 'vscode';
import { commands } from 'vscode';
import { commands, type Uri } from 'vscode';

/**
* Executes command to decrypt local settings file.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { gitignoreFileName, hostFileName, localSettingsFileName, logicAppKind } from '../../../../../constants';
import {
azureWebJobsStorageKey,
gitignoreFileName,
hostFileName,
localEmulatorConnectionString,
localSettingsFileName,
logicAppKind,
} from '../../../../../constants';
import { addDefaultBundle } from '../../../../utils/bundleFeed';
import { confirmOverwriteFile, writeFormattedJson } from '../../../../utils/fs';
import { ProjectCodeCreateStepBase } from '../../CodeProjectBase/ProjectCodeCreateStepBase';
Expand Down Expand Up @@ -59,7 +66,7 @@ export class ScriptProjectCreateStep extends ProjectCodeCreateStepBase {
const localSettingsJson: ILocalSettingsJson = {
IsEncrypted: false,
Values: {
AzureWebJobsStorage: 'UseDevelopmentStorage=true',
[azureWebJobsStorageKey]: localEmulatorConnectionString,
WORKFLOWS_SUBSCRIPTION_ID: '',
FUNCTIONS_WORKER_RUNTIME: 'node',
APP_KIND: logicAppKind,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { gitignoreFileName, hostFileName, localSettingsFileName, logicAppKind, workerRuntimeKey } from '../../../../constants';
import {
ProjectDirectoryPath,
appKindSetting,
azureWebJobsStorageKey,
gitignoreFileName,
hostFileName,
localSettingsFileName,
logicAppKind,
workerRuntimeKey,
} from '../../../../constants';
import { addDefaultBundle } from '../../../utils/bundleFeed';
import { confirmOverwriteFile, writeFormattedJson } from '../../../utils/fs';
import { getFunctionsWorkerRuntime } from '../../../utils/vsCodeConfig/settings';
Expand Down Expand Up @@ -47,9 +56,9 @@ export class ScriptProjectCreateStep extends ProjectCreateStepBase {
const localSettingsJson: ILocalSettingsJson = {
IsEncrypted: false,
Values: {
AzureWebJobsStorage: '',
APP_KIND: logicAppKind,
ProjectDirectoryPath: path.join(context.projectPath),
[azureWebJobsStorageKey]: '',
[appKindSetting]: logicAppKind,
[ProjectDirectoryPath]: path.join(context.projectPath),
},
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { WorkerRuntime } from '@microsoft/vscode-extension-logic-apps';
import {
ProjectDirectoryPath,
appKindSetting,
designTimeDirectoryName,
designerStartApi,
hostFileContent,
hostFileName,
localSettingsFileName,
logicAppKind,
workerRuntimeKey,
} from '../../../constants';
import { ext } from '../../../extensionVariables';
import { localize } from '../../../localize';
import { addOrUpdateLocalAppSettings } from '../../utils/appSettings/localSettings';
import { addOrUpdateLocalAppSettings, getLocalSettingsSchema } from '../../utils/appSettings/localSettings';
import {
createJsonFile,
getOrCreateDesignTimeDirectory,
Expand All @@ -17,7 +21,7 @@ import {
waitForDesignTimeStartUp,
} from '../../utils/codeless/startDesignTimeApi';
import { getFunctionsCommand } from '../../utils/funcCoreTools/funcVersion';
import { backendRuntimeBaseUrl, settingsFileContent } from './extensionConfig';
import { backendRuntimeBaseUrl } from './extensionConfig';
import type { IActionContext } from '@microsoft/vscode-azext-utils';
import * as portfinder from 'portfinder';
import { ProgressLocation, type Uri, window } from 'vscode';
Expand All @@ -43,17 +47,22 @@ export async function startBackendRuntime(projectPath: string, context: IActionC
return;
}

const modifiedSettingsFileContent = { ...settingsFileContent };
modifiedSettingsFileContent.Values.ProjectDirectoryPath = projectPath;
const settingsFileContent = getLocalSettingsSchema(true, projectPath);

try {
if (designTimeDirectory) {
await createJsonFile(designTimeDirectory, hostFileName, hostFileContent);
await createJsonFile(designTimeDirectory, localSettingsFileName, modifiedSettingsFileContent);
await addOrUpdateLocalAppSettings(context, designTimeDirectory.fsPath, {
APP_KIND: logicAppKind,
ProjectDirectoryPath: projectPath,
});
await createJsonFile(designTimeDirectory, localSettingsFileName, settingsFileContent);
await addOrUpdateLocalAppSettings(
context,
designTimeDirectory.fsPath,
{
[appKindSetting]: logicAppKind,
[ProjectDirectoryPath]: projectPath,
[workerRuntimeKey]: WorkerRuntime.Node,
},
true
);
const cwd: string = designTimeDirectory.fsPath;
const portArgs = `--port ${ext.designTimePort}`;
startDesignTimeProcess(ext.outputChannel, cwd, getFunctionsCommand(), 'host', 'start', portArgs);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { logicAppKind } from '../../../constants';

export const webviewType = 'dataMapperWebview';

export const supportedDataMapDefinitionFileExts = ['.lml', '.yml'];
Expand All @@ -17,14 +15,4 @@ export const draftMapDefinitionSuffix = '.draft';
export const mapDefinitionExtension = '.lml';
export const mapXsltExtension = '.xslt';

export const settingsFileContent = {
IsEncrypted: false,
Values: {
AzureWebJobsSecretStorageType: 'Files',
FUNCTIONS_WORKER_RUNTIME: 'dotnet-isolated',
ProjectDirectoryPath: 'should/be/set/by/code',
APP_KIND: logicAppKind,
},
};

export const backendRuntimeBaseUrl = 'http://localhost:';
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { describe, it, expect, vi } from 'vitest';
import { getLocalSettingsSchema } from '../localSettings';
import {
ProjectDirectoryPath,
appKindSetting,
azureStorageTypeSetting,
azureWebJobsSecretStorageTypeKey,
azureWebJobsStorageKey,
localEmulatorConnectionString,
workerRuntimeKey,
} from '../../../../constants';

describe('utils/appSettings', () => {
describe('getLocalSettingsSchema', () => {
const projectPath = 'path/to/project';

it('Should have IsEncrypted property and Values property have basic schema', () => {
const settings = getLocalSettingsSchema(true);
expect(settings).toHaveProperty('IsEncrypted', false);
expect(settings).toHaveProperty('Values');
expect(settings['Values']).toHaveProperty(appKindSetting);
expect(settings['Values']).toHaveProperty(workerRuntimeKey);
});

it('Should not have ProjectDirectoryPath when project path param is not sent', () => {
const settings = getLocalSettingsSchema(true);
expect(settings).not.toHaveProperty(ProjectDirectoryPath);
});

it('Should have the AzureWebJobsSecretStorageType when is design time localsettings and have ProjectDirectoryPath property when sent', () => {
const settings = getLocalSettingsSchema(true, projectPath);
expect(settings['Values']).toHaveProperty(azureWebJobsSecretStorageTypeKey, azureStorageTypeSetting);
expect(settings['Values']).toHaveProperty(ProjectDirectoryPath, projectPath);
});

it('Should have the AzureWebJobsStorage when is not design time localsettings and have ProjectDirectoryPath property when sent', () => {
const settings = getLocalSettingsSchema(false, projectPath);
expect(settings['Values']).toHaveProperty(azureWebJobsStorageKey, localEmulatorConnectionString);
expect(settings['Values']).toHaveProperty(ProjectDirectoryPath, projectPath);
});
});
});
54 changes: 41 additions & 13 deletions apps/vs-code-designer/src/app/utils/appSettings/localSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { azureWebJobsStorageKey, localSettingsFileName } from '../../../constants';
import {
azureWebJobsStorageKey,
localSettingsFileName,
ProjectDirectoryPath,
appKindSetting,
azureWebJobsSecretStorageTypeKey,
localEmulatorConnectionString,
logicAppKind,
workerRuntimeKey,
azureStorageTypeSetting,
} from '../../../constants';
import { localize } from '../../../localize';
import { decryptLocalSettings } from '../../commands/appSettings/decryptLocalSettings';
import { encryptLocalSettings } from '../../commands/appSettings/encryptLocalSettings';
Expand All @@ -11,26 +21,27 @@ import { writeFormattedJson } from '../fs';
import { parseJson } from '../parseJson';
import { DialogResponses, parseError } from '@microsoft/vscode-azext-utils';
import type { IActionContext } from '@microsoft/vscode-azext-utils';
import { MismatchBehavior } from '@microsoft/vscode-extension-logic-apps';
import { MismatchBehavior, WorkerRuntime } from '@microsoft/vscode-extension-logic-apps';
import type { ILocalSettingsJson } from '@microsoft/vscode-extension-logic-apps';
import * as fse from 'fs-extra';
import * as path from 'path';
import type { MessageItem } from 'vscode';
import { Uri } from 'vscode';
import { Uri, type MessageItem } from 'vscode';

/**
* Updates local.settings.json file.
* @param {IActionContext} context - Command context.
* @param {string} projectPath - Project path with local.settings.json file.
* @param {boolean} settingsToAdd - Settings data to updata.
* @param {boolean} isDesignTime - A flag indicating whether it is design time or not.
*/
export async function addOrUpdateLocalAppSettings(
context: IActionContext,
projectPath: string,
settingsToAdd: Record<string, string>
settingsToAdd: Record<string, string>,
isDesignTime = false
): Promise<void> {
const localSettingsPath: string = path.join(projectPath, localSettingsFileName);
const settings: ILocalSettingsJson = await getLocalSettingsJson(context, localSettingsPath);
const settings: ILocalSettingsJson = await getLocalSettingsJson(context, localSettingsPath, isDesignTime);

settings.Values = settings.Values || {};
settings.Values = {
Expand Down Expand Up @@ -71,12 +82,14 @@ async function getDecriptedLocalSettings(
* @param {IActionContext} context - Command context.
* @param {string} localSettingsPath - File path.
* @param {boolean} allowOverwrite - Allow overwrite on file.
* @param {boolean} isDesignTime - A flag indicating whether it is design time or not.
* @returns {Promise<ILocalSettingsJson>} local.setting.json file.
*/
export async function getLocalSettingsJson(
context: IActionContext,
localSettingsPath: string,
allowOverwrite = false
allowOverwrite = false,
isDesignTime = false
): Promise<ILocalSettingsJson> {
if (fse.existsSync(localSettingsPath)) {
const data: string = (await fse.readFile(localSettingsPath)).toString();
Expand Down Expand Up @@ -115,12 +128,7 @@ export async function getLocalSettingsJson(
}
}

return {
IsEncrypted: false,
Values: {
AzureWebJobsStorage: '',
},
};
return getLocalSettingsSchema(isDesignTime);
}

/**
Expand Down Expand Up @@ -176,3 +184,23 @@ export async function getAzureWebJobsStorage(context: IActionContext, projectPat
const settings: ILocalSettingsJson = await getLocalSettingsJson(context, path.join(projectPath, localSettingsFileName));
return settings.Values && settings.Values[azureWebJobsStorageKey];
}

/**
* Retrieves the local settings schema based on the project path and design time flag.
* @param {boolean} isDesignTime - A flag indicating whether it is design time or not.
* @param {string} projectPath - The path of the project.
* @returns The local settings schema.
*/
export const getLocalSettingsSchema = (isDesignTime: boolean, projectPath?: string) => {
return {
IsEncrypted: false,
Values: {
[appKindSetting]: logicAppKind,
[workerRuntimeKey]: WorkerRuntime.Node,
...(projectPath ? { [ProjectDirectoryPath]: projectPath } : {}),
...(isDesignTime
? { [azureWebJobsSecretStorageTypeKey]: azureStorageTypeSetting }
: { [azureWebJobsStorageKey]: localEmulatorConnectionString }),
},
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type StandardApp, ProjectType } from '@microsoft/vscode-extension-logic
import { describe, it, expect } from 'vitest';
import { getCodelessWorkflowTemplate, getFunctionWorkflowTemplate } from '../templates';
import { workflowKind } from '../../../../constants';
import { isNullOrEmpty, isRecordNotEmpty } from '@microsoft/logic-apps-shared';
import { isNullOrEmpty } from '@microsoft/logic-apps-shared';

const methodName = 'testMethod';

Expand Down
35 changes: 18 additions & 17 deletions apps/vs-code-designer/src/app/utils/codeless/startDesignTimeApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ import {
showStartDesignTimeMessageSetting,
designerApiLoadTimeout,
type hostFileContent,
workerRuntimeKey,
appKindSetting,
} from '../../../constants';
import { ext } from '../../../extensionVariables';
import { localize } from '../../../localize';
import type { settingsFileContent } from '../../commands/dataMapper/extensionConfig';
import { addOrUpdateLocalAppSettings } from '../appSettings/localSettings';
import { addOrUpdateLocalAppSettings, getLocalSettingsSchema } from '../appSettings/localSettings';
import { updateFuncIgnore } from '../codeless/common';
import { writeFormattedJson } from '../fs';
import { getFunctionsCommand } from '../funcCoreTools/funcVersion';
Expand All @@ -35,6 +36,7 @@ import {
type IAzExtOutputChannel,
callWithTelemetryAndErrorHandling,
} from '@microsoft/vscode-azext-utils';
import type { ILocalSettingsJson } from '@microsoft/vscode-extension-logic-apps';
import { WorkerRuntime } from '@microsoft/vscode-extension-logic-apps';
import axios from 'axios';
import * as cp from 'child_process';
Expand Down Expand Up @@ -63,14 +65,7 @@ export async function startDesignTimeApi(projectPath: string): Promise<void> {
},
},
};
const settingsFileContent: any = {
IsEncrypted: false,
Values: {
AzureWebJobsSecretStorageType: 'Files',
FUNCTIONS_WORKER_RUNTIME: WorkerRuntime.Node,
APP_KIND: logicAppKind,
},
};

if (!ext.designTimePort) {
ext.designTimePort = await portfinder.getPortPromise();
}
Expand All @@ -88,15 +83,21 @@ export async function startDesignTimeApi(projectPath: string): Promise<void> {
);

const designTimeDirectory: Uri | undefined = await getOrCreateDesignTimeDirectory(designTimeDirectoryName, projectPath);
settingsFileContent.Values[ProjectDirectoryPath] = projectPath;
const settingsFileContent = getLocalSettingsSchema(true, projectPath);

if (designTimeDirectory) {
await createJsonFile(designTimeDirectory, hostFileName, hostFileContent);
await createJsonFile(designTimeDirectory, localSettingsFileName, settingsFileContent);
await addOrUpdateLocalAppSettings(actionContext, designTimeDirectory.fsPath, {
APP_KIND: logicAppKind,
ProjectDirectoryPath: projectPath,
});
await addOrUpdateLocalAppSettings(
actionContext,
designTimeDirectory.fsPath,
{
[appKindSetting]: logicAppKind,
[ProjectDirectoryPath]: projectPath,
[workerRuntimeKey]: WorkerRuntime.Node,
},
true
);
await updateFuncIgnore(projectPath, [`${designTimeDirectoryName}/`]);
const cwd: string = designTimeDirectory.fsPath;
const portArgs = `--port ${ext.designTimePort}`;
Expand Down Expand Up @@ -243,13 +244,13 @@ export async function promptStartDesignTimeOption(context: IActionContext) {
* If the file already exists, it will not be overwritten.
* @param {Uri} directory - The directory where the file will be created.
* @param {string} fileName - The name of the file to be created.
* @param {hostFileContent | settingsFileContent}fileContent - The content of the file to be created.
* @param {hostFileContent | ILocalSettingsJson}fileContent - The content of the file to be created.
* @returns A Promise that resolves when the file is created successfully.
*/
export async function createJsonFile(
directory: Uri,
fileName: string,
fileContent: typeof hostFileContent | typeof settingsFileContent
fileContent: typeof hostFileContent | ILocalSettingsJson
): Promise<void> {
const filePath: Uri = Uri.file(path.join(directory.fsPath, fileName));

Expand Down
3 changes: 1 addition & 2 deletions apps/vs-code-designer/src/app/utils/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { localize } from '../../localize';
import { isEmptyString } from '@microsoft/logic-apps-shared';
import type { IActionContext } from '@microsoft/vscode-azext-utils';
import { DialogResponses } from '@microsoft/vscode-azext-utils';
import { DialogResponses, type IActionContext } from '@microsoft/vscode-azext-utils';
import type { pathRelativeFunc } from '@microsoft/vscode-extension-logic-apps';
import * as crypto from 'crypto';
import * as fse from 'fs-extra';
Expand Down
Loading

0 comments on commit b010740

Please sign in to comment.