diff --git a/src/mongo/shell.ts b/src/mongo/shell.ts index e05452262..7888122fc 100644 --- a/src/mongo/shell.ts +++ b/src/mongo/shell.ts @@ -15,10 +15,16 @@ export class Shell { private onResult: EventEmitter<{ exitCode, result, stderr, code?: string, message?: string }> = new EventEmitter<{ exitCode, result, stderr, code?: string, message?: string }>(); - public static create(execPath: string, connectionString: string): Promise { + public static create(execPath: string, connectionString: string, isEmulator: boolean): Promise { return new Promise((c, e) => { try { - const shellProcess = cp.spawn(execPath, ['--quiet', connectionString]); + let args = ['--quiet', connectionString]; + if (isEmulator) { + // Without this the connection will fail due to the self-signed DocDB certificate + args.push("--ssl"); + args.push("--sslAllowInvalidCertificates"); + } + const shellProcess = cp.spawn(execPath, args); return c(new Shell(execPath, shellProcess)); } catch (error) { e(`Error while creating mongo shell with path '${execPath}': ${error}`); diff --git a/src/mongo/tree/MongoAccountTreeItem.ts b/src/mongo/tree/MongoAccountTreeItem.ts index 63f5a4ef7..08f59d894 100644 --- a/src/mongo/tree/MongoAccountTreeItem.ts +++ b/src/mongo/tree/MongoAccountTreeItem.ts @@ -64,7 +64,7 @@ export class MongoAccountTreeItem implements IAzureParentTreeItem { } return databases .filter((database: IDatabaseInfo) => !(database.name && database.name.toLowerCase() === "admin" && database.empty)) // Filter out the 'admin' database if it's empty - .map(database => new MongoDatabaseTreeItem(database.name, this.connectionString, node.id)); + .map(database => new MongoDatabaseTreeItem(database.name, this.connectionString, this.isEmulator, node.id)); } catch (error) { return [{ @@ -95,7 +95,7 @@ export class MongoAccountTreeItem implements IAzureParentTreeItem { if (collectionName) { showCreatingNode(databaseName); - const databaseTreeItem = new MongoDatabaseTreeItem(databaseName, this.connectionString, node.id); + const databaseTreeItem = new MongoDatabaseTreeItem(databaseName, this.connectionString, this.isEmulator, node.id); await databaseTreeItem.createCollection(collectionName); return databaseTreeItem; } diff --git a/src/mongo/tree/MongoDatabaseTreeItem.ts b/src/mongo/tree/MongoDatabaseTreeItem.ts index 384a55d3e..be3d3a73b 100644 --- a/src/mongo/tree/MongoDatabaseTreeItem.ts +++ b/src/mongo/tree/MongoDatabaseTreeItem.ts @@ -18,13 +18,15 @@ export class MongoDatabaseTreeItem implements IAzureParentTreeItem { public readonly contextValue: string = MongoDatabaseTreeItem.contextValue; public readonly childTypeLabel: string = "Collection"; public readonly connectionString: string; + public readonly isEmulator: boolean; public readonly databaseName: string; private _parentId: string; - constructor(databaseName: string, connectionString: string, parentId: string) { + constructor(databaseName: string, connectionString: string, isEmulator: boolean, parentId: string) { this.databaseName = databaseName; this.connectionString = connectionString; + this.isEmulator = isEmulator; this._parentId = parentId; } @@ -100,14 +102,14 @@ export class MongoDatabaseTreeItem implements IAzureParentTreeItem { return result; } } - return reportProgress(this.executeCommandInShell(command, context), 'Executing command'); + return withProgress(this.executeCommandInShell(command, context), 'Executing command'); }); } if (command.name === 'createCollection') { - return reportProgress(this.createCollection(stripQuotes(command.arguments.join(','))).then(() => JSON.stringify({ 'Created': 'Ok' })), 'Creating collection'); + return withProgress(this.createCollection(stripQuotes(command.arguments.join(','))).then(() => JSON.stringify({ 'Created': 'Ok' })), 'Creating collection'); } else { - return reportProgress(this.executeCommandInShell(command, context), 'Executing command'); + return withProgress(this.executeCommandInShell(command, context), 'Executing command'); } } @@ -152,7 +154,7 @@ export class MongoDatabaseTreeItem implements IAzureParentTreeItem { } private async createShell(shellPath: string): Promise { - return >Shell.create(shellPath, this.connectionString) + return >Shell.create(shellPath, this.connectionString, this.isEmulator) .then( shell => { return shell.useDatabase(this.databaseName).then(() => shell); @@ -176,7 +178,7 @@ export function validateMongoCollectionName(collectionName: string): string | un return undefined; } -function reportProgress(promise: Thenable, title: string): Thenable { +function withProgress(promise: Thenable, title: string): Thenable { return vscode.window.withProgress( { location: vscode.ProgressLocation.Window, diff --git a/src/tree/AttachedAccountsTreeItem.ts b/src/tree/AttachedAccountsTreeItem.ts index 7d5621744..3f481bf41 100644 --- a/src/tree/AttachedAccountsTreeItem.ts +++ b/src/tree/AttachedAccountsTreeItem.ts @@ -163,7 +163,8 @@ export class AttachedAccountsTreeItem implements IAzureParentTreeItem { } if (port) { if (defaultExperience.api === API.MongoDB) { - connectionString = `mongodb://localhost:C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==@localhost:${port}?ssl=true`; + // Mongo shell doesn't parse passwords with slashes, so we need to URI encode it + connectionString = `mongodb://localhost:${encodeURIComponent('C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==')}@localhost:${port}/?ssl=true`; } else { connectionString = `AccountEndpoint=https://localhost:${port}/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==;`;