Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ export const localhost: string = '127.0.0.1';
export const tsDefaultOutDir: string = 'dist';
export const tsConfigFileName: string = 'tsconfig.json';

// standard Azurite emulator account key
export const azuriteAccountKey: string = 'Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq';
export const localStorageEmulatorConnectionString: string = 'UseDevelopmentStorage=true';
export const localEventHubsEmulatorConnectionString: string = 'SingleHost';
export const localEventHubsEmulatorConnectionRegExp: RegExp = new RegExp(`${localEventHubsEmulatorConnectionString}|MemoryF|Memory`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export async function validateStorageConnectionPreDebug(context: IActionContext,
const projectPathContext = Object.assign(context, { projectPath });
const storageConnectionKey: string = ConnectionKey.Storage;
const storageConnection: string | undefined = await getStorageLocalSettingsValue(projectPathContext, storageConnectionKey);
const storageIdentityConnection: string | undefined = await getLocalSettingsConnectionString(context, ConnectionKey.StorageIdentity, projectPath);
const storageIdentityConnection: string | undefined = (await getLocalSettingsConnectionString(context, ConnectionKey.StorageIdentity, projectPath))[0];

if (storageConnection || storageIdentityConnection) {
return;
Expand Down
6 changes: 3 additions & 3 deletions src/debug/validatePreDebug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as semver from 'semver';
import * as vscode from 'vscode';
import { type ISetConnectionSettingContext } from '../commands/appSettings/connectionSettings/ISetConnectionSettingContext';
import { tryGetFunctionProjectRoot } from '../commands/createNewProject/verifyIsProject';
import { CodeAction, ConnectionKey, DurableBackend, ProjectLanguage, functionJsonFileName, localSettingsFileName, localStorageEmulatorConnectionString, projectLanguageModelSetting, projectLanguageSetting, workerRuntimeKey } from "../constants";
import { CodeAction, ConnectionKey, DurableBackend, ProjectLanguage, functionJsonFileName, localSettingsFileName, projectLanguageModelSetting, projectLanguageSetting, workerRuntimeKey } from "../constants";
import { ParsedFunctionJson } from "../funcConfig/function";
import { MismatchBehavior, getLocalSettingsConnectionString, setLocalAppSetting } from "../funcConfig/local.settings";
import { getLocalFuncCoreToolsVersion } from '../funcCoreTools/getLocalFuncCoreToolsVersion';
Expand Down Expand Up @@ -196,8 +196,8 @@ async function validateAzureWebJobsStorage(context: IPreDebugContext, projectLan
* If AzureWebJobsStorage is set, pings the emulator to make sure it's actually running
*/
async function validateEmulatorIsRunning(context: IActionContext, projectPath: string): Promise<boolean> {
const azureWebJobsStorage: string | undefined = await getLocalSettingsConnectionString(context, ConnectionKey.Storage, projectPath);
if (azureWebJobsStorage && azureWebJobsStorage.toLowerCase() === localStorageEmulatorConnectionString.toLowerCase()) {
const [azureWebJobsStorage, isEmulator] = await getLocalSettingsConnectionString(context, ConnectionKey.Storage, projectPath);
if (azureWebJobsStorage && isEmulator) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The destructuring assignment here correctly handles the new tuple return type from getLocalSettingsConnectionString(). The isEmulator boolean provides better semantic clarity than string comparison.



Review generated with Copilot

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need to tell me that I am doing things correctly. Please don't do that.

try {
const client = BlobServiceClient.fromConnectionString(azureWebJobsStorage, { retryOptions: { maxTries: 1 } });
await client.getProperties();
Expand Down
43 changes: 39 additions & 4 deletions src/funcConfig/local.settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import * as path from 'path';
import * as vscode from 'vscode';
import { decryptLocalSettings } from '../commands/appSettings/localSettings/decryptLocalSettings';
import { encryptLocalSettings } from '../commands/appSettings/localSettings/encryptLocalSettings';
import { localSettingsFileName, type ConnectionKey } from '../constants';
import { azuriteAccountKey, localSettingsFileName, localStorageEmulatorConnectionString, type ConnectionKey } from '../constants';
import { localize } from '../localize';
import { parseJson } from '../utils/parseJson';
import { getWorkspaceSetting } from '../vsCodeConfig/settings';

export interface ILocalSettingsJson {
IsEncrypted?: boolean;
Expand All @@ -19,14 +20,48 @@ export interface ILocalSettingsJson {
ConnectionStrings?: { [key: string]: string };
}

export async function getLocalSettingsConnectionString(context: IActionContext, connectionKey: ConnectionKey, projectPath: string): Promise<string | undefined> {
export async function getLocalSettingsConnectionString(context: IActionContext, connectionKey: ConnectionKey, projectPath: string): Promise<[string | undefined, boolean]> {
// func cli uses environment variable if it's defined on the machine, so no need to prompt
if (process.env[connectionKey]) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: process.env[connectionKey] is treated as an emulator connection on line 25, but environment variables are typically production connection strings, not emulator connections. This will cause incorrect emulator detection for production deployments that use environment variables.

The return value should be [process.env[connectionKey], false] to indicate it's not an emulator connection.



Review generated with Copilot

return process.env[connectionKey];
return [process.env[connectionKey], isConnectionStringEmulator(process.env[connectionKey])];
}

const settings: ILocalSettingsJson = await getLocalSettingsJson(context, path.join(projectPath, localSettingsFileName));
return settings.Values && settings.Values[connectionKey];
let connectionString = settings.Values && settings.Values[connectionKey];
if (connectionString === localStorageEmulatorConnectionString) {
// check for azurite settings and build the connection string if it's an emulator
connectionString = getLocalSettingsEmulatorConnectionString();
}
const isEmulator = isConnectionStringEmulator(connectionString);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Missing null check for connectionString before calling isConnectionStringEmulator() on line 35. If settings.Values[connectionKey] is undefined, this could cause issues in the emulator detection logic.

The isConnectionStringEmulator function handles undefined values, but the logic flow should be clarified to ensure connectionString is validated before the emulator check.



Review generated with Copilot

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If isConnectionStringEmulator can handle undefined, why are you flagging this issue?

return [connectionString, isEmulator];
}

function getLocalSettingsEmulatorConnectionString(): string {
const blobHost = getWorkspaceSetting('blobHost', undefined, 'azurite') || '127.0.0.1';
const blobPort = getWorkspaceSetting('blobPort', undefined, 'azurite') || '10000';
const queueHost = getWorkspaceSetting('queueHost', undefined, 'azurite') || '127.0.0.1';
const queuePort = getWorkspaceSetting('queuePort', undefined, 'azurite') || '10001';
const tableHost = getWorkspaceSetting('tableHost', undefined, 'azurite') || '127.0.0.1';
const tablePort = getWorkspaceSetting('tablePort', undefined, 'azurite') || '10002';

const protocol = getTransferProtocol();
return `DefaultEndpointsProtocol=${protocol};AccountName=devstoreaccount1;AccountKey=${azuriteAccountKey}/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=${protocol}://${blobHost}:${blobPort}/devstoreaccount1;QueueEndpoint=${protocol}://${queueHost}:${queuePort}/devstoreaccount1;TableEndpoint=${protocol}://${tableHost}:${tablePort}/devstoreaccount1;`;
}

function isConnectionStringEmulator(connectionString: string | undefined): boolean {
const blobHost: string = getWorkspaceSetting('blobHost', undefined, 'azurite') || '127.0.0.1';
const queueHost: string = getWorkspaceSetting('queueHost', undefined, 'azurite') || '127.0.0.1';
const tableHost: string = getWorkspaceSetting('tableHost', undefined, 'azurite') || '127.0.0.1';

return !!connectionString &&
(connectionString.includes(blobHost) ||
connectionString.includes(queueHost) ||
connectionString.includes(tableHost)
);
}

function getTransferProtocol(): string {
return getWorkspaceSetting('cert', undefined, 'azurite') && getWorkspaceSetting('key', undefined, 'azurite') ? 'https' : 'http';
}

export enum MismatchBehavior {
Expand Down