Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor emulator configuration to use encapsulated objects #2572

Closed
Closed
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
11 changes: 11 additions & 0 deletions src/commands/newConnection/CoreEmulatorConfiguration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

export interface CoreEmulatorConfiguration {
/**
* Indicates if the connection is to an emulator.
*/
isEmulator: boolean;
}
16 changes: 16 additions & 0 deletions src/commands/newConnection/MongoEmulatorConfiguration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

export interface MongoEmulatorConfiguration {
/**
* Indicates if the connection is to an emulator.
*/
isEmulator: boolean;

/**
* Indicates if the emulator security should be disabled.
*/
disableEmulatorSecurity: boolean;
}
2 changes: 1 addition & 1 deletion src/commands/newConnection/MongoExecuteStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class MongoExecuteStep extends AzureWizardExecuteStep<NewConnectionWizard
const storageItem: SharedWorkspaceStorageItem = {
id: parsedCS.username + '@' + parsedCS.redact().toString(),
name: label,
properties: { isEmulator: context.mongodbapiIsEmulator ?? false, api },
properties: { isEmulator: context.emulatorConfiguration?.isEmulator ?? false, api },
secrets: [connectionString],
};

Expand Down
3 changes: 2 additions & 1 deletion src/commands/newConnection/NewConnectionWizardContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type ConnectionString from 'mongodb-connection-string-url';
import { type Experience } from '../../AzureDBExperiences';
import { type ParsedDocDBConnectionString } from '../../docdb/docDBConnectionStrings';
import { type QuickPickType } from '../../utils/pickItem/pickExperience';
import { type MongoEmulatorConfiguration } from './MongoEmulatorConfiguration';

export interface NewConnectionWizardContext extends IActionContext {
quickPickType: QuickPickType;
Expand All @@ -20,5 +21,5 @@ export interface NewConnectionWizardContext extends IActionContext {
username?: string;
password?: string;

mongodbapiIsEmulator?: boolean;
emulatorConfiguration?: MongoEmulatorConfiguration;
}
5 changes: 3 additions & 2 deletions src/commands/newEmulatorConnection/ExecuteStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
NewEmulatorConnectionMode,
type NewEmulatorConnectionWizardContext,
} from './NewEmulatorConnectionWizardContext';
import { type MongoEmulatorConfiguration } from '../newConnection/MongoEmulatorConfiguration';

export class ExecuteStep extends AzureWizardExecuteStep<NewEmulatorConnectionWizardContext> {
public priority: number = 100;
Expand Down Expand Up @@ -55,9 +56,9 @@ export class ExecuteStep extends AzureWizardExecuteStep<NewEmulatorConnectionWiz
name: label,
properties: {
api: experience.api,
isEmulator: true,
isEmulator: (context.emulatorConfiguration as MongoEmulatorConfiguration)?.isEmulator ?? true,
// only adds 'disableEmulatorSecurity' when it's set (for Mongo)
...(context.disableMongoEmulatorSecurity && { disableEmulatorSecurity: true }),
...((context.emulatorConfiguration as MongoEmulatorConfiguration)?.disableEmulatorSecurity && { disableEmulatorSecurity: true }),
},
secrets: [connectionString],
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import { type IActionContext } from '@microsoft/vscode-azext-utils';
import { type Experience } from '../../AzureDBExperiences';
import { type MongoEmulatorConfiguration } from '../newConnection/MongoEmulatorConfiguration';

export enum NewEmulatorConnectionMode {
Preconfigured = 'preconfigured', // using a preconfigured emulator
Expand All @@ -19,9 +20,7 @@ export interface NewEmulatorConnectionWizardContext extends IActionContext {
connectionString?: string;
port?: number;

// Currently specific to MongoDB Emulator, allows the user to override the default TLS/SSL configuration (e.g. disable it)
// It's only relevant for the MongoDB Emulator
disableMongoEmulatorSecurity?: boolean;
emulatorConfiguration?: MongoEmulatorConfiguration;

// The selected mode; defaults to Unknown
mode?: NewEmulatorConnectionMode;
Expand Down
4 changes: 3 additions & 1 deletion src/docdb/commands/executeNoSqlQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import * as vscodeUtil from '../../utils/vscodeUtils';
import { noSqlQueryConnectionKey, type NoSqlQueryConnection } from '../NoSqlCodeLensProvider';
import { getCosmosClient, type CosmosDBCredential } from '../getCosmosClient';
import { type MongoEmulatorConfiguration } from '../newConnection/MongoEmulatorConfiguration';

export async function executeNoSqlQuery(
_context: IActionContext,
Expand Down Expand Up @@ -44,7 +45,8 @@
credentials.push({ type: 'key', key: masterKey });
}
credentials.push({ type: 'auth', tenantId: tenantId });
const client = getCosmosClient(endpoint, credentials, isEmulator);
const emulatorConfiguration: MongoEmulatorConfiguration = { isEmulator, disableEmulatorSecurity: false };
const client = getCosmosClient(endpoint, credentials, emulatorConfiguration.isEmulator);

Check failure on line 49 in src/docdb/commands/executeNoSqlQuery.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe argument of type error typed assigned to a parameter of type `boolean | undefined`

Check failure on line 49 in src/docdb/commands/executeNoSqlQuery.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe member access .isEmulator on an `error` typed value

Check failure on line 49 in src/docdb/commands/executeNoSqlQuery.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe argument of type error typed assigned to a parameter of type `boolean | undefined`

Check failure on line 49 in src/docdb/commands/executeNoSqlQuery.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe member access .isEmulator on an `error` typed value
const options = { populateQueryMetrics };
const response = await client
.database(databaseId)
Expand Down
4 changes: 3 additions & 1 deletion src/docdb/commands/getNoSqlQueryPlan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import * as vscodeUtil from '../../utils/vscodeUtils';
import { noSqlQueryConnectionKey, type NoSqlQueryConnection } from '../NoSqlCodeLensProvider';
import { getCosmosClient, type CosmosDBCredential } from '../getCosmosClient';
import { type MongoEmulatorConfiguration } from '../newConnection/MongoEmulatorConfiguration';

export async function getNoSqlQueryPlan(
_context: IActionContext,
Expand Down Expand Up @@ -39,7 +40,8 @@
credentials.push({ type: 'key', key: masterKey });
}
credentials.push({ type: 'auth', tenantId: tenantId });
const client = getCosmosClient(endpoint, credentials, isEmulator);
const emulatorConfiguration: MongoEmulatorConfiguration = { isEmulator, disableEmulatorSecurity: false };
const client = getCosmosClient(endpoint, credentials, emulatorConfiguration.isEmulator);

Check failure on line 44 in src/docdb/commands/getNoSqlQueryPlan.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe argument of type error typed assigned to a parameter of type `boolean | undefined`

Check failure on line 44 in src/docdb/commands/getNoSqlQueryPlan.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe member access .isEmulator on an `error` typed value

Check failure on line 44 in src/docdb/commands/getNoSqlQueryPlan.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe argument of type error typed assigned to a parameter of type `boolean | undefined`

Check failure on line 44 in src/docdb/commands/getNoSqlQueryPlan.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe member access .isEmulator on an `error` typed value
const response = await client.database(databaseId).container(containerId).getQueryPlan(queryText);
await vscodeUtil.showNewFile(
JSON.stringify(response.result, undefined, 2),
Expand Down
11 changes: 5 additions & 6 deletions src/mongo/MongoScrapbookService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { type MongoCommand } from './MongoCommand';
import { findCommandAtPosition, getAllCommandsFromText } from './MongoScrapbookHelpers';
import { MongoShellScriptRunner } from './MongoShellScriptRunner';
import { MongoCodeLensProvider } from './services/MongoCodeLensProvider';
import { type MongoEmulatorConfiguration } from '../commands/newConnection/MongoEmulatorConfiguration';

export class MongoScrapbookServiceImpl {
//--------------------------------------------------------------------------------
Expand Down Expand Up @@ -46,8 +47,8 @@ export class MongoScrapbookServiceImpl {
await ext.mongoLanguageClient.connect(
CredentialCache.getConnectionStringWithPassword(this._cluster.id),
this._database.name,
cluster.isEmulator ?? false,
cluster.disableEmulatorSecurity ?? false,
(cluster.emulatorConfiguration as MongoEmulatorConfiguration)?.isEmulator ?? false,
(cluster.emulatorConfiguration as MongoEmulatorConfiguration)?.disableEmulatorSecurity ?? false,
);
}

Expand Down Expand Up @@ -126,8 +127,7 @@ export class MongoScrapbookServiceImpl {

const shellRunner = await MongoShellScriptRunner.createShell(context, {
connectionString: CredentialCache.getConnectionStringWithPassword(this.getClusterId()!),
isEmulator: CredentialCache.isEmulator(this.getClusterId()!),
disableEmulatorSecurity: CredentialCache.disableEmulatorSecurity(this.getClusterId()!),
emulatorConfiguration: CredentialCache.getEmulatorConfiguration(this.getClusterId()!) as MongoEmulatorConfiguration,
});

try {
Expand Down Expand Up @@ -240,8 +240,7 @@ export class MongoScrapbookServiceImpl {
if (!shellRunner) {
shellRunner = await MongoShellScriptRunner.createShell(context, {
connectionString: CredentialCache.getConnectionStringWithPassword(this.getClusterId()!),
isEmulator: CredentialCache.isEmulator(this.getClusterId()!),
disableEmulatorSecurity: CredentialCache.disableEmulatorSecurity(this.getClusterId()!),
emulatorConfiguration: CredentialCache.getEmulatorConfiguration(this.getClusterId()!) as MongoEmulatorConfiguration,
});
ephemeralShell = true;
}
Expand Down
11 changes: 5 additions & 6 deletions src/mongo/MongoShellScriptRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { InteractiveChildProcess } from '../utils/InteractiveChildProcess';
import { randomUtils } from '../utils/randomUtils';
import { getBatchSizeSetting } from '../utils/workspacUtils';
import { wrapError } from '../utils/wrapError';
import { type MongoEmulatorConfiguration } from '../commands/newConnection/MongoEmulatorConfiguration';

const mongoExecutableFileName = process.platform === 'win32' ? 'mongo.exe' : 'mongosh';

Expand Down Expand Up @@ -44,16 +45,15 @@ export class MongoShellScriptRunner extends vscode.Disposable {
execPath: string,
execArgs: string[],
connectionString: string,
isEmulator: boolean | undefined,
disableEmulatorSecurity: boolean | undefined,
emulatorConfiguration: MongoEmulatorConfiguration,
outputChannel: vscode.OutputChannel,
timeoutSeconds: number,
): Promise<MongoShellScriptRunner> {
try {
const args: string[] = execArgs.slice() || []; // Snapshot since we modify it
args.push(connectionString);

if (isEmulator && disableEmulatorSecurity) {
if (emulatorConfiguration.isEmulator && emulatorConfiguration.disableEmulatorSecurity) {
// Without these the connection will fail due to the self-signed DocDB certificate
if (args.indexOf('--tlsAllowInvalidCertificates') < 0) {
args.push('--tlsAllowInvalidCertificates');
Expand Down Expand Up @@ -123,7 +123,7 @@ export class MongoShellScriptRunner extends vscode.Disposable {

public static async createShell(
context: IActionContext,
connectionInfo: { connectionString: string; isEmulator: boolean; disableEmulatorSecurity: boolean },
connectionInfo: { connectionString: string; emulatorConfiguration: MongoEmulatorConfiguration },
): Promise<MongoShellScriptRunner> {
const config = vscode.workspace.getConfiguration();
let shellPath: string | undefined = config.get(ext.settingsKeys.mongoShellPath);
Expand All @@ -146,8 +146,7 @@ export class MongoShellScriptRunner extends vscode.Disposable {
shellPath,
shellArgs,
connectionInfo.connectionString,
connectionInfo.isEmulator,
connectionInfo.disableEmulatorSecurity,
connectionInfo.emulatorConfiguration,
ext.outputChannel,
timeout,
);
Expand Down
5 changes: 3 additions & 2 deletions src/mongo/services/IConnectionParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { MongoEmulatorConfiguration } from '../commands/newConnection/MongoEmulatorConfiguration';

Check failure on line 6 in src/mongo/services/IConnectionParams.ts

View workflow job for this annotation

GitHub Actions / Build

All imports in the declaration are only used as types. Use `import type`

Check failure on line 6 in src/mongo/services/IConnectionParams.ts

View workflow job for this annotation

GitHub Actions / Build

All imports in the declaration are only used as types. Use `import type`

export interface IConnectionParams {
connectionString: string;
databaseName: string;
extensionUserAgent: string;
isEmulator: boolean;
disableEmulatorSecurity: boolean;
emulatorConfiguration: MongoEmulatorConfiguration;
}
5 changes: 3 additions & 2 deletions src/mongo/services/languageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@
});

connection.onRequest('connect', (connectionParams: IConnectionParams) => {
const emulatorConfiguration = connectionParams.emulatorConfiguration;

Check failure on line 58 in src/mongo/services/languageService.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe assignment of an error typed value

Check failure on line 58 in src/mongo/services/languageService.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe assignment of an error typed value
void connectToMongoClient(
connectionParams.connectionString,
connectionParams.extensionUserAgent,
connectionParams.isEmulator,
connectionParams.disableEmulatorSecurity,
emulatorConfiguration.isEmulator,

Check failure on line 62 in src/mongo/services/languageService.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe argument of type error typed assigned to a parameter of type `boolean | undefined`

Check failure on line 62 in src/mongo/services/languageService.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe member access .isEmulator on an `error` typed value

Check failure on line 62 in src/mongo/services/languageService.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe argument of type error typed assigned to a parameter of type `boolean | undefined`

Check failure on line 62 in src/mongo/services/languageService.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe member access .isEmulator on an `error` typed value
emulatorConfiguration.disableEmulatorSecurity,

Check failure on line 63 in src/mongo/services/languageService.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe argument of type error typed assigned to a parameter of type `boolean | undefined`

Check failure on line 63 in src/mongo/services/languageService.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe member access .disableEmulatorSecurity on an `error` typed value

Check failure on line 63 in src/mongo/services/languageService.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe argument of type error typed assigned to a parameter of type `boolean | undefined`

Check failure on line 63 in src/mongo/services/languageService.ts

View workflow job for this annotation

GitHub Actions / Build

Unsafe member access .disableEmulatorSecurity on an `error` typed value
).then((account) => {
this.db = account.db(connectionParams.databaseName);
void this.schemaService.registerSchemas(this.db).then((schemas) => {
Expand Down
25 changes: 7 additions & 18 deletions src/mongoClusters/CredentialCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
*--------------------------------------------------------------------------------------------*/

import { addAuthenticationDataToConnectionString } from './utils/connectionStringHelpers';
import { MongoEmulatorConfiguration } from '../commands/newConnection/MongoEmulatorConfiguration';

export interface MongoClustersCredentials {
mongoClusterId: string;
connectionStringWithPassword?: string;
connectionString: string;
connectionUser: string;
isEmulator?: boolean;
disableEmulatorSecurity?: boolean;
emulatorConfiguration?: MongoEmulatorConfiguration;
}

export class CredentialCache {
Expand All @@ -26,12 +26,8 @@ export class CredentialCache {
return CredentialCache._store.has(mongoClusterId) as boolean;
}

public static isEmulator(mongoClusterId: string): boolean {
return CredentialCache._store.get(mongoClusterId)?.isEmulator as boolean;
}

public static disableEmulatorSecurity(mongoClusterId: string): boolean {
return CredentialCache._store.get(mongoClusterId)?.disableEmulatorSecurity as boolean;
public static getEmulatorConfiguration(mongoClusterId: string): MongoEmulatorConfiguration | undefined {
return CredentialCache._store.get(mongoClusterId)?.emulatorConfiguration;
}

public static getCredentials(mongoClusterId: string): MongoClustersCredentials | undefined {
Expand All @@ -42,27 +38,21 @@ export class CredentialCache {
CredentialCache._store.delete(mongoClusterId);
}

/**
*
* @param connectionString connection string with credentials
*/
/**
* Sets the credentials for a given connection string and stores them in the credential cache.
*
* @param id - The credential id. It's supposed to be the same as the tree item id of the mongo cluster item to simplify the lookup.
* @param connectionString - The connection string to which the credentials will be added.
* @param username - The username to be used for authentication.
* @param password - The password to be used for authentication.
* @param isEmulator - Indicates whether the account is an emulator. It is optional as it's only relevant for workspace items
* @param disableEmulatorSecurity - Indicates whether the emulator security is disabled. It is optional as it's only relevant for workspace items
* @param emulatorConfiguration - The emulator configuration object.
*/
public static setCredentials(
mongoClusterId: string,
connectionString: string,
username: string,
password: string,
isEmulator?: boolean,
disableEmulatorSecurity?: boolean,
emulatorConfiguration?: MongoEmulatorConfiguration,
): void {
const connectionStringWithPassword = addAuthenticationDataToConnectionString(
connectionString,
Expand All @@ -75,8 +65,7 @@ export class CredentialCache {
connectionStringWithPassword: connectionStringWithPassword,
connectionString: connectionString,
connectionUser: username,
isEmulator: isEmulator,
disableEmulatorSecurity: disableEmulatorSecurity,
emulatorConfiguration: emulatorConfiguration,
};

CredentialCache._store.set(mongoClusterId, credentials);
Expand Down
15 changes: 7 additions & 8 deletions src/mongoClusters/MongoClustersClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { CredentialCache } from './CredentialCache';
import { areMongoDBAzure, getHostsFromConnectionString } from './utils/connectionStringHelpers';
import { getMongoClusterMetadata, type MongoClusterMetadata } from './utils/getMongoClusterMetadata';
import { toFilterQueryObj } from './utils/toFilterQuery';
import { MongoEmulatorConfiguration } from '../commands/newConnection/MongoEmulatorConfiguration';

export interface DatabaseItemModel {
name: string;
Expand Down Expand Up @@ -64,8 +65,7 @@ export class MongoClustersClient {
static _clients: Map<string, MongoClustersClient> = new Map();

private _mongoClient: MongoClient;
private isEmulator: boolean;
private disableEmulatorSecurity: boolean;
private emulatorConfiguration: MongoEmulatorConfiguration;

/**
* Use getClient instead of a constructor. Connections/Client are being cached and reused.
Expand All @@ -86,8 +86,7 @@ export class MongoClustersClient {
const userAgentString = areMongoDBAzure(hosts) ? appendExtensionUserAgent() : undefined;

const cStringPassword = CredentialCache.getConnectionStringWithPassword(this.credentialId);
this.isEmulator = CredentialCache.isEmulator(this.credentialId);
this.disableEmulatorSecurity = CredentialCache.disableEmulatorSecurity(this.credentialId);
this.emulatorConfiguration = CredentialCache.getEmulatorConfiguration(this.credentialId);

// Prepare the options object and prepare the appName
// appname appears to be the correct equivalent to user-agent for mongo
Expand All @@ -96,10 +95,10 @@ export class MongoClustersClient {
appName: userAgentString,
};

if (this.isEmulator) {
if (this.emulatorConfiguration?.isEmulator) {
mongoClientOptions.serverSelectionTimeoutMS = 4000;

if (this.disableEmulatorSecurity) {
if (this.emulatorConfiguration?.disableEmulatorSecurity) {
// Prevents self signed certificate error for emulator https://github.com/microsoft/vscode-cosmosdb/issues/1241#issuecomment-614446198
mongoClientOptions.tlsAllowInvalidCertificates = true;
}
Expand All @@ -109,10 +108,10 @@ export class MongoClustersClient {
this._mongoClient = await MongoClient.connect(cStringPassword as string, mongoClientOptions);
} catch (error) {
const message = parseError(error).message;
if (this.isEmulator && message.includes('ECONNREFUSED')) {
if (this.emulatorConfiguration?.isEmulator && message.includes('ECONNREFUSED')) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
error.message = `Unable to connect to local Mongo DB emulator. Make sure it is started correctly. See ${Links.LocalConnectionDebuggingTips} for tips.`;
} else if (this.isEmulator && message.includes('self-signed certificate')) {
} else if (this.emulatorConfiguration?.isEmulator && message.includes('self-signed certificate')) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
error.message = `The local Mongo DB emulator is using a self-signed certificate. To connect to the emulator, you must import the emulator's TLS/SSL certificate. See ${Links.LocalConnectionDebuggingTips} for tips.`;
}
Expand Down
Loading