Skip to content

Commit 87484f8

Browse files
fix(shell): launch shell does not work with SSH VSCODE-327 (#402)
1 parent 6da1e37 commit 87484f8

14 files changed

+412
-382
lines changed

package-lock.json

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

package.json

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
"release-draft": "node ./scripts/release-draft.js"
6262
},
6363
"engines": {
64-
"vscode": "^1.58.1",
64+
"vscode": "^1.65.2",
6565
"node": "^14.17.5",
6666
"npm": "^8.3.1"
6767
},
@@ -901,7 +901,7 @@
901901
}
902902
},
903903
"dependencies": {
904-
"@babel/parser": "^7.17.3",
904+
"@babel/parser": "^7.17.7",
905905
"@babel/traverse": "^7.17.3",
906906
"@fortawesome/fontawesome-svg-core": "^1.3.0",
907907
"@fortawesome/free-solid-svg-icons": "^6.0.0",
@@ -910,21 +910,21 @@
910910
"@iconify/react": "^1.1.4",
911911
"@leafygreen-ui/logo": "^6.1.0",
912912
"@leafygreen-ui/toggle": "^7.0.5",
913-
"@mongosh/browser-runtime-electron": "^1.2.2",
914-
"@mongosh/i18n": "^1.2.2",
915-
"@mongosh/service-provider-server": "^1.2.2",
916-
"@mongosh/shell-api": "^1.2.2",
913+
"@mongosh/browser-runtime-electron": "^1.2.3",
914+
"@mongosh/i18n": "^1.2.3",
915+
"@mongosh/service-provider-server": "^1.2.3",
916+
"@mongosh/shell-api": "^1.2.3",
917917
"analytics-node": "^5.1.2",
918918
"bson": "^4.6.1",
919-
"bson-transpilers": "^1.6.0",
919+
"bson-transpilers": "^1.7.0",
920920
"classnames": "^2.3.1",
921921
"debug": "^4.3.3",
922922
"dotenv": "^8.6.0",
923923
"micromatch": "^4.0.4",
924-
"mongodb": "^4.4.0",
924+
"mongodb": "^4.4.1",
925925
"mongodb-cloud-info": "^1.1.3",
926926
"mongodb-connection-string-url": "^2.5.2",
927-
"mongodb-data-service": "^21.17.0",
927+
"mongodb-data-service": "^21.18.0",
928928
"mongodb-ns": "^2.3.0",
929929
"mongodb-schema": "^9.0.0",
930930
"numeral": "^2.0.6",
@@ -951,11 +951,11 @@
951951
"@types/jest": "^26.0.24",
952952
"@types/mocha": "^8.2.3",
953953
"@types/node": "^14.18.12",
954-
"@types/react": "^17.0.39",
955-
"@types/react-dom": "^17.0.11",
954+
"@types/react": "^17.0.40",
955+
"@types/react-dom": "^17.0.13",
956956
"@types/sinon": "^9.0.11",
957957
"@types/uuid": "^8.3.4",
958-
"@types/vscode": "^1.58.1",
958+
"@types/vscode": "^1.65.0",
959959
"@typescript-eslint/eslint-plugin": "^4.33.0",
960960
"@typescript-eslint/parser": "^4.33.0",
961961
"autoprefixer": "^9.8.8",
@@ -1002,7 +1002,7 @@
10021002
"ts-jest": "^26.5.6",
10031003
"ts-loader": "^8.3.0",
10041004
"ts-node": "^9.1.1",
1005-
"typescript": "^4.5.5",
1005+
"typescript": "^4.6.2",
10061006
"vsce": "^1.103.1",
10071007
"vscode-test": "^1.6.1",
10081008
"webpack": "^4.46.0",

snippets/stage-autocompleter.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,29 @@
131131
],
132132
"description": "Allows for multiple parellel aggregations to be specified."
133133
},
134+
"MongoDB Aggregations $fill": {
135+
"prefix": "$fill",
136+
"body": [
137+
"/**",
138+
" * sortBy: Syntax is the same as \\$sort, required if \"method\" is used in at least one output spec otherwise optional",
139+
" * partitionBy: Optional, default is a single partition. Specification is the same as _id in \\$group (same as partitionBy in window functions).",
140+
" * partitionByFields: Optional, set of fields that acts as a compound key to define each partition.",
141+
" * output - Required, object for each field to fill in. For a single field, can be a single object.",
142+
" * output.<field> - A field to be filled with value, if missing or null in the current document.",
143+
" */",
144+
"\\$fill: {",
145+
" sortBy: ${1:sortSpec},",
146+
" partitionBy: ${2:expression},",
147+
" partitionByFields: [${3:string}, ${4:string}, ...],",
148+
" output: {",
149+
" field1: {value: ${5:expression}},",
150+
" field2: {method: ${6:string}},",
151+
" ...",
152+
" }",
153+
"}"
154+
],
155+
"description": "Populates null and missing field values within documents."
156+
},
134157
"MongoDB Aggregations $geoNear": {
135158
"prefix": "$geoNear",
136159
"body": [

src/commands/launchMongoShell.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,7 @@ const openMongoDBShell = (connectionController: ConnectionController): Promise<b
7777
return Promise.resolve(false);
7878
}
7979

80-
const activeMongoClientOptions = connectionController.getMongoClientConnectionOptions();
81-
82-
if (!activeMongoClientOptions) {
83-
void vscode.window.showErrorMessage(
84-
'No active connection found.'
85-
);
86-
87-
return Promise.resolve(false);
88-
}
89-
90-
const mdbConnectionString = activeMongoClientOptions.url || '';
80+
const mdbConnectionString = connectionController.getActiveConnectionString();
9181

9282
if (userShell.includes('powershell.exe')) {
9383
launchMongoDBShellOnPowershell(shellCommand, mdbConnectionString);

src/connectionController.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,43 @@ export default class ConnectionController {
713713
: '';
714714
}
715715

716+
_getConnectionStringWithProxy(mongoClientConnectionOptions: { url: string; options: MongoClientOptions; }): string {
717+
const connectionStringData = new ConnectionString(mongoClientConnectionOptions.url);
718+
719+
if (mongoClientConnectionOptions.options.proxyHost) {
720+
connectionStringData.searchParams.set('proxyHost', mongoClientConnectionOptions.options.proxyHost);
721+
}
722+
723+
if (mongoClientConnectionOptions.options.proxyPassword) {
724+
connectionStringData.searchParams.set('proxyPassword', mongoClientConnectionOptions.options.proxyPassword);
725+
}
726+
727+
if (mongoClientConnectionOptions.options.proxyPort) {
728+
connectionStringData.searchParams.set('proxyPort', `${mongoClientConnectionOptions.options.proxyPort}`);
729+
}
730+
731+
if (mongoClientConnectionOptions.options.proxyUsername) {
732+
connectionStringData.searchParams.set('proxyUsername', mongoClientConnectionOptions.options.proxyUsername);
733+
}
734+
735+
return connectionStringData.toString();
736+
}
737+
738+
getActiveConnectionString(): string {
739+
const mongoClientConnectionOptions = this.getMongoClientConnectionOptions();
740+
const connectionString = mongoClientConnectionOptions?.url;
741+
742+
if (!connectionString) {
743+
throw new Error('Connection string not found.');
744+
}
745+
746+
if (mongoClientConnectionOptions?.options.proxyHost) {
747+
return this._getConnectionStringWithProxy(mongoClientConnectionOptions);
748+
}
749+
750+
return connectionString;
751+
}
752+
716753
getActiveDataService(): DataService | null {
717754
return this._activeDataService;
718755
}
@@ -730,7 +767,6 @@ export default class ConnectionController {
730767
}
731768

732769
const url = new ConnectionString(connectionOptions.connectionString);
733-
734770
url.searchParams.delete('appname');
735771
return url.toString();
736772
}

src/explorer/helpTree.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ export default class HelpTree implements vscode.TreeDataProvider<vscode.TreeItem
101101
'report'
102102
);
103103

104-
const segmentAnonymousId = this._telemetryService?.getSegmentAnonymousId();
105-
const ajsAid = segmentAnonymousId ? `&ajs_aid=${segmentAnonymousId}` : '';
104+
const telemetryUserIdentity = this._telemetryService?.getTelemetryUserIdentity();
105+
const ajsAid = telemetryUserIdentity ? `&ajs_aid=${telemetryUserIdentity[0]}` : '';
106106
const atlas = new HelpLinkTreeItem(
107107
'Create Free Atlas Cluster',
108108
`https://mongodb.com/products/vs-code/vs-code-atlas-signup?utm_campaign=vs-code-extension&utm_source=visual-studio&utm_medium=product${ajsAid}`,

src/storage/storageController.ts

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -67,34 +67,27 @@ export default class StorageController {
6767
}
6868

6969
getUserIdentity() {
70-
return {
71-
userId: this.getUserId(),
72-
anonymousId: this.getAnonymousId()
73-
};
74-
}
75-
76-
getAnonymousId() {
77-
let globalAnonymousId = this.get(StorageVariables.GLOBAL_ANONYMOUS_ID);
78-
79-
if (globalAnonymousId && typeof globalAnonymousId === 'string') {
80-
return globalAnonymousId;
81-
}
82-
70+
const globalAnonymousId = this.get(StorageVariables.GLOBAL_ANONYMOUS_ID);
8371
const globalUserId = this.get(StorageVariables.GLOBAL_USER_ID);
8472

73+
// Initially, we used `userId` as Segment user identifier, but this usage is being deprecated.
74+
// The `anonymousId` should be used instead.
75+
// We keep sending `userId` to Segment for old users though to preserve their analytics.
8576
if (globalUserId && typeof globalUserId === 'string') {
86-
globalAnonymousId = globalUserId;
87-
} else {
88-
globalAnonymousId = uuidv4();
77+
return {
78+
userId: globalUserId
79+
};
8980
}
9081

91-
void this.update(StorageVariables.GLOBAL_ANONYMOUS_ID, globalAnonymousId);
92-
93-
return globalAnonymousId;
94-
}
82+
if (globalAnonymousId && typeof globalAnonymousId === 'string') {
83+
return {
84+
anonymousId: globalAnonymousId
85+
};
86+
}
9587

96-
getUserId(): string | undefined {
97-
return this.get(StorageVariables.GLOBAL_USER_ID);
88+
const anonymousId = uuidv4();
89+
void this.update(StorageVariables.GLOBAL_ANONYMOUS_ID, anonymousId);
90+
return { anonymousId };
9891
}
9992

10093
async saveConnectionToStore(storeConnectionInfo: StoreConnectionInfo): Promise<void> {

src/telemetry/telemetryService.ts

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@ import { StorageController } from '../storage';
1818
const log = createLogger('telemetry');
1919
const { version } = require('../../package.json');
2020

21-
interface AnalyticsNodeIdentity {
22-
userId?: string;
23-
anonymousId: string;
24-
}
25-
2621
type PlaygroundTelemetryEventProperties = {
2722
type: string | null;
2823
partial: boolean;
@@ -32,7 +27,7 @@ type PlaygroundTelemetryEventProperties = {
3227
export type SegmentProperties = {
3328
event: string;
3429
userId?: string;
35-
anonymousId: string;
30+
anonymousId?: string;
3631
properties: unknown;
3732
};
3833

@@ -91,8 +86,8 @@ export enum TelemetryEventTypes {
9186
*/
9287
export default class TelemetryService {
9388
_segmentAnalytics?: SegmentAnalytics;
94-
_segmentUserId?: string; // Should exist only for users prior v0.9.0.
95-
_segmentAnonymousId: string; // The randomly generated uuid.
89+
_segmentUserId?: string;
90+
_segmentAnonymousId?: string;
9691
_segmentKey?: string; // The segment API write key.
9792

9893
private _context: vscode.ExtensionContext;
@@ -162,13 +157,9 @@ export default class TelemetryService {
162157
flushInterval: 10000 // 10 seconds is the default libraries' value.
163158
});
164159

165-
const identity: AnalyticsNodeIdentity = {
166-
anonymousId: this._segmentAnonymousId
167-
};
168-
if (this._segmentUserId) {
169-
identity.userId = this._segmentUserId;
170-
}
171-
this._segmentAnalytics.identify(identity);
160+
const segmentProperties = this.getTelemetryUserIdentity();
161+
this._segmentAnalytics.identify(segmentProperties);
162+
log.info('TELEMETRY identify', segmentProperties);
172163
}
173164
}
174165

@@ -204,9 +195,8 @@ export default class TelemetryService {
204195
): void {
205196
if (this._isTelemetryFeatureEnabled()) {
206197
const segmentProperties: SegmentProperties = {
198+
...this.getTelemetryUserIdentity(),
207199
event: eventType,
208-
userId: this._segmentUserId,
209-
anonymousId: this._segmentAnonymousId,
210200
properties: {
211201
...properties,
212202
extension_version: `${version}`
@@ -276,8 +266,16 @@ export default class TelemetryService {
276266
return 'other';
277267
}
278268

279-
getSegmentAnonymousId(): string {
280-
return this._segmentAnonymousId;
269+
getTelemetryUserIdentity() {
270+
if (this._segmentUserId) {
271+
return {
272+
userId: this._segmentUserId
273+
};
274+
}
275+
276+
return {
277+
anonymousId: this._segmentAnonymousId
278+
};
281279
}
282280

283281
trackPlaygroundCodeExecuted(

src/test/suite/connectionController.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,4 +1038,18 @@ suite('Connection Controller Test Suite', function () {
10381038
}
10391039
);
10401040
});
1041+
1042+
test('_getConnectionStringWithProxy returns string with proxy options', () => {
1043+
const expectedConnectionStringWithProxy = 'mongodb://localhost:27018/?appname=mongodb-vscode+0.0.0-dev.0&proxyHost=localhost&proxyPassword=gwce7tr8733ujbr&proxyPort=3378&proxyUsername=test';
1044+
const connectionString = testConnectionController._getConnectionStringWithProxy({
1045+
url: 'mongodb://localhost:27018/?appname=mongodb-vscode+0.0.0-dev.0',
1046+
options: {
1047+
proxyHost: 'localhost',
1048+
proxyPassword: 'gwce7tr8733ujbr',
1049+
proxyPort: 3378,
1050+
proxyUsername: 'test',
1051+
}
1052+
});
1053+
assert.strictEqual(connectionString, expectedConnectionStringWithProxy);
1054+
});
10411055
});

src/test/suite/explorer/helpExplorer.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,10 @@ suite('Help Explorer Test Suite', function () {
4242
const atlasHelpItem = helpTreeItems[5];
4343

4444
assert.strictEqual(atlasHelpItem.label, 'Create Free Atlas Cluster');
45+
const telemetryUserIdentity = mdbTestExtension.testExtensionController._telemetryService.getTelemetryUserIdentity();
4546
assert.strictEqual(
4647
atlasHelpItem.url,
47-
`https://mongodb.com/products/vs-code/vs-code-atlas-signup?utm_campaign=vs-code-extension&utm_source=visual-studio&utm_medium=product&ajs_aid=${mdbTestExtension.testExtensionController._telemetryService.getSegmentAnonymousId()}`
48+
`https://mongodb.com/products/vs-code/vs-code-atlas-signup?utm_campaign=vs-code-extension&utm_source=visual-studio&utm_medium=product&ajs_aid=${telemetryUserIdentity[0]}`
4849
);
4950
assert.strictEqual(atlasHelpItem.iconName, 'atlas');
5051
assert.strictEqual(atlasHelpItem.linkId, 'freeClusterCTA');

0 commit comments

Comments
 (0)