Skip to content

Commit 2be1e0d

Browse files
feat: connect with ssh to shell (#114)
1 parent 9593a32 commit 2be1e0d

File tree

7 files changed

+500
-266
lines changed

7 files changed

+500
-266
lines changed

package-lock.json

Lines changed: 397 additions & 188 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -608,18 +608,18 @@
608608
"@fortawesome/free-solid-svg-icons": "^5.13.0",
609609
"@fortawesome/react-fontawesome": "^0.1.9",
610610
"@leafygreen-ui/toggle": "^3.0.0",
611-
"@mongosh/browser-runtime-electron": "0.0.1-alpha.15",
612-
"@mongosh/service-provider-server": "0.0.1-alpha.15",
613-
"@mongosh/shell-api": "0.0.1-alpha.15",
611+
"@mongosh/browser-runtime-electron": "0.0.1-alpha.17",
612+
"@mongosh/service-provider-server": "0.0.1-alpha.17",
613+
"@mongosh/shell-api": "0.0.1-alpha.17",
614614
"analytics-node": "^3.4.0-beta.1",
615615
"bson": "^4.0.3",
616616
"classnames": "^2.2.6",
617617
"debug": "^4.1.1",
618618
"dotenv": "^8.2.0",
619619
"encoding": "^0.1.12",
620620
"mongodb-cloud-info": "^1.1.2",
621-
"mongodb-connection-model": "^16.1.0",
622-
"mongodb-data-service": "^16.7.0",
621+
"mongodb-connection-model": "^16.1.2",
622+
"mongodb-data-service": "^16.8.0",
623623
"mongodb-ns": "^2.2.0",
624624
"mongodb-schema": "^8.2.5",
625625
"react": "^16.13.1",

src/connectionController.ts

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ const MAX_CONNECTION_NAME_LENGTH = 512;
2020
export enum DataServiceEventTypes {
2121
CONNECTIONS_DID_CHANGE = 'CONNECTIONS_DID_CHANGE',
2222
ACTIVE_CONNECTION_CHANGED = 'ACTIVE_CONNECTION_CHANGED',
23-
ACTIVE_CONNECTION_CHANGING = 'ACTIVE_CONNECTION_CHANGING',
23+
ACTIVE_CONNECTION_CHANGING = 'ACTIVE_CONNECTION_CHANGING'
2424
}
2525

2626
export enum ConnectionTypes {
2727
CONNECTION_FORM = 'CONNECTION_FORM',
2828
CONNECTION_STRING = 'CONNECTION_STRING',
29-
CONNECTION_ID = 'CONNECTION_ID',
29+
CONNECTION_ID = 'CONNECTION_ID'
3030
}
3131

3232
export type SavedConnectionInformation = {
@@ -258,17 +258,25 @@ export default class ConnectionController {
258258
connectionModel: ConnectionModelType,
259259
connectionType: ConnectionTypes
260260
): Promise<boolean> => {
261-
const { driverUrl, instanceId } = connectionModel.getAttributes({
261+
const {
262+
driverUrl,
263+
instanceId,
264+
sshTunnelOptions
265+
} = connectionModel.getAttributes({
262266
derived: true
263267
});
264268
const connectionId = uuidv4();
265269
const connectionInformation: SavedConnectionInformation = {
266270
connectionModel,
267271
driverUrl
268272
};
273+
const name =
274+
sshTunnelOptions.host && sshTunnelOptions.port
275+
? `${sshTunnelOptions.host}:${sshTunnelOptions.port}`
276+
: instanceId;
269277
const savedConnection: SavedConnection = {
270278
id: connectionId,
271-
name: instanceId,
279+
name,
272280
// To begin we just store it on the session, the storage controller
273281
// handles changing this based on user preference.
274282
storageLocation: StorageScope.NONE
@@ -578,13 +586,13 @@ export default class ConnectionController {
578586
const connectionNameToRemove:
579587
| string
580588
| undefined = await vscode.window.showQuickPick(
581-
connectionIds.map(
582-
(id, index) => `${index + 1}: ${this._connections[id].name}`
583-
),
584-
{
585-
placeHolder: 'Choose a connection to remove...'
586-
}
587-
);
589+
connectionIds.map(
590+
(id, index) => `${index + 1}: ${this._connections[id].name}`
591+
),
592+
{
593+
placeHolder: 'Choose a connection to remove...'
594+
}
595+
);
588596

589597
if (!connectionNameToRemove) {
590598
return Promise.resolve(false);

src/connectionModelType.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ type ConnectionAttributes = {
33
driverUrlWithSsh: string;
44
driverOptions: any;
55
instanceId: string;
6+
sshTunnelOptions: any;
67
};
78

89
export type ConnectionModelType = {

src/mdbExtensionController.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -399,23 +399,31 @@ export default class MDBExtensionController implements vscode.Disposable {
399399

400400
public openMongoDBShell(): Promise<boolean> {
401401
let mdbConnectionString;
402+
402403
if (this._connectionController) {
403-
const activeConnectionDriverUrl = this._connectionController.getActiveConnectionDriverUrl();
404-
mdbConnectionString = activeConnectionDriverUrl
405-
? activeConnectionDriverUrl
404+
const activeConnectionModel = this._connectionController
405+
.getActiveConnectionModel()
406+
?.getAttributes({ derived: true });
407+
408+
mdbConnectionString = activeConnectionModel
409+
? activeConnectionModel.driverUrlWithSsh
406410
: '';
407411
}
412+
408413
if (!mdbConnectionString) {
409414
vscode.window.showErrorMessage(
410415
'You need to be connected before launching the MongoDB Shell.'
411416
);
417+
412418
return Promise.resolve(false);
413419
}
420+
414421
const mongoDBShell = vscode.window.createTerminal({
415422
name: 'MongoDB Shell',
416423
env: { MDB_CONNECTION_STRING: mdbConnectionString }
417424
});
418425
const shellCommand = vscode.workspace.getConfiguration('mdb').get('shell');
426+
419427
mongoDBShell.sendText(
420428
`${shellCommand} $MDB_CONNECTION_STRING; unset MDB_CONNECTION_STRING`
421429
);

src/test/suite/extension.test.ts

Lines changed: 65 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,37 @@
11
import * as assert from 'assert';
22
import * as vscode from 'vscode';
3-
import { before, after } from 'mocha';
3+
import { beforeEach, afterEach } from 'mocha';
44
import * as sinon from 'sinon';
5-
5+
import Connection = require('mongodb-connection-model/lib/model');
66
import MDBExtensionController from '../../mdbExtensionController';
7-
87
import { TestExtensionContext } from './stubs';
9-
import { TEST_DATABASE_URI } from './dbTestHelper';
10-
import ConnectionController from '../../connectionController';
11-
import { StorageController } from '../../storage';
12-
import { StatusView } from '../../views';
13-
import TelemetryController from '../../telemetry/telemetryController';
148

159
suite('Extension Test Suite', () => {
1610
vscode.window.showInformationMessage('Starting tests...');
1711

18-
const disposables: vscode.Disposable[] = [];
1912
const mockExtensionContext = new TestExtensionContext();
2013
const mockMDBExtension = new MDBExtensionController(mockExtensionContext);
21-
const mockStorageController = new StorageController(mockExtensionContext);
22-
const testTelemetryController = new TelemetryController(
23-
mockStorageController,
24-
mockExtensionContext
25-
);
26-
const testConnectionController = new ConnectionController(
27-
new StatusView(mockExtensionContext),
28-
mockStorageController,
29-
testTelemetryController
30-
);
3114
const sandbox = sinon.createSandbox();
15+
3216
let fakeShowErrorMessage: any;
17+
let fakeGetActiveConnectionModel: any;
18+
let createTerminalSpy: any;
3319

34-
before(() => {
20+
beforeEach(() => {
3521
sandbox.stub(vscode.window, 'showInformationMessage');
22+
3623
fakeShowErrorMessage = sandbox.stub(vscode.window, 'showErrorMessage');
24+
fakeGetActiveConnectionModel = sandbox.stub(
25+
mockMDBExtension._connectionController,
26+
'getActiveConnectionModel'
27+
);
28+
29+
createTerminalSpy = sinon.spy(vscode.window, 'createTerminal');
3730
});
3831

39-
after(() => {
40-
disposables.forEach((d) => d.dispose());
41-
disposables.length = 0;
32+
afterEach(() => {
4233
sandbox.restore();
34+
sinon.restore();
4335
});
4436

4537
test('commands are registered in vscode', async () => {
@@ -102,54 +94,69 @@ suite('Extension Test Suite', () => {
10294
});
10395

10496
test('openMongoDBShell should open a terminal with the active connection driver url', async () => {
105-
try {
106-
const succesfullyConnected = await testConnectionController.addNewConnectionStringAndConnect(
107-
TEST_DATABASE_URI
108-
);
97+
const driverUri =
98+
'mongodb://localhost:27018/?readPreference=primary&ssl=false';
10999

110-
assert(
111-
succesfullyConnected === true,
112-
'Expected a successful connection response.'
113-
);
100+
fakeGetActiveConnectionModel.returns(
101+
new Connection({
102+
hostname: 'localhost',
103+
port: 27018
104+
})
105+
);
114106

107+
try {
115108
await mockMDBExtension.openMongoDBShell();
116109

117-
const spyActiveConnectionDriverUrl = sinon.spy(
118-
testConnectionController,
119-
'getActiveConnectionDriverUrl'
110+
assert(createTerminalSpy.called);
111+
112+
const terminalOptions: vscode.TerminalOptions =
113+
createTerminalSpy.firstCall.args[0];
114+
115+
assert(
116+
terminalOptions.env?.MDB_CONNECTION_STRING === driverUri,
117+
`Expected open terminal to set env var 'MDB_CONNECTION_STRING' to ${driverUri} found ${terminalOptions.env?.MDB_CONNECTION_STRING}`
120118
);
121-
const createTerminalSpy = sinon.spy(vscode.window, 'createTerminal');
122119

123-
const checkResult = async () => {
124-
await testConnectionController.disconnect();
120+
await mockMDBExtension._connectionController.disconnect();
121+
mockMDBExtension._connectionController.clearAllConnections();
122+
} catch (e) {
123+
assert(false);
124+
return;
125+
}
126+
});
125127

126-
try {
127-
assert(spyActiveConnectionDriverUrl.called);
128-
assert(createTerminalSpy.called);
128+
test('openMongoDBShell should open a terminal with ssh tunnel port injected', async () => {
129+
const driverUri =
130+
'mongodb://127.0.0.1:27017/?readPreference=primary&ssl=false';
129131

130-
const expectedUri =
131-
'mongodb://localhost:27018/?readPreference=primary&ssl=false';
132+
fakeGetActiveConnectionModel.returns(
133+
new Connection({
134+
hostname: '127.0.0.1',
135+
sshTunnel: 'USER_PASSWORD',
136+
sshTunnelHostname: 'my.ssh-server.com',
137+
sshTunnelUsername: 'my-user',
138+
sshTunnelPassword: 'password'
139+
})
140+
);
132141

133-
const terminalOptions: vscode.TerminalOptions = createTerminalSpy.firstCall.args[0];
142+
try {
143+
await mockMDBExtension.openMongoDBShell();
134144

135-
assert(
136-
terminalOptions.env?.MDB_CONNECTION_STRING ===
137-
expectedUri,
138-
`Expected open terminal to set env var 'MDB_CONNECTION_STRING' to ${expectedUri} found ${
139-
terminalOptions.env?.MDB_CONNECTION_STRING
140-
}`
141-
);
145+
assert(createTerminalSpy.called);
142146

143-
testConnectionController.clearAllConnections();
144-
} catch (e) {
145-
assert(false);
146-
return;
147-
}
148-
};
147+
const terminalOptions: vscode.TerminalOptions =
148+
createTerminalSpy.firstCall.args[0];
149149

150-
disposables.push(vscode.window.onDidOpenTerminal(checkResult));
151-
} catch (error) {
150+
assert(
151+
terminalOptions.env?.MDB_CONNECTION_STRING !== driverUri,
152+
`Expected open terminal to set env var 'MDB_CONNECTION_STRING' to driver url with ssh tunnel port injected found ${terminalOptions.env?.MDB_CONNECTION_STRING}`
153+
);
154+
155+
await mockMDBExtension._connectionController.disconnect();
156+
mockMDBExtension._connectionController.clearAllConnections();
157+
} catch (e) {
152158
assert(false);
159+
return;
153160
}
154161
});
155162
});

src/test/suite/telemetry/telemetryController.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ suite('Telemetry Controller Test Suite', () => {
172172
sinon.assert.called(mockTrackPlaygroundCodeExecuted);
173173
});
174174

175-
test('track playground loaded and saved events', async () => {
175+
test('track playground loaded and saved events', async function () {
176+
this.timeout(3000);
176177
await loadAndSavePlayground(getDocUri('test.mongodb'));
177178

178179
sinon.assert.called(mockTrackPlaygroundLoadedMethod);

0 commit comments

Comments
 (0)