Skip to content

Commit 7c1d4a7

Browse files
fix: use user id and anonymous id as segment identity VSCODE-326 (#394)
* fix: use user id and anonymous id as segment identity VSCODE-326 * test: try to fix fluky export to language test * refactor: add AnalyticsNodeIdentity interface * test: try config set msvs_version 2017 * build: try with node-gyp@latest * build: try windows-2019
1 parent 4279aab commit 7c1d4a7

File tree

15 files changed

+167
-82
lines changed

15 files changed

+167
-82
lines changed

.github/workflows/test-and-build.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414

1515
strategy:
1616
matrix:
17-
os: [ubuntu-latest, windows-latest, macos-latest]
17+
os: [ubuntu-latest, windows-2019, macos-latest]
1818

1919
fail-fast: false
2020

@@ -51,7 +51,7 @@ jobs:
5151
run: |
5252
npm config delete node_gyp
5353
npm uninstall --global node-gyp
54-
npm install --global node-gyp
54+
npm install --global node-gyp@latest
5555
npm prefix -g | % {npm config set node_gyp "$_\node_modules\node-gyp\bin\node-gyp.js"}
5656
5757
- name: Install Dependencies

package-lock.json

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

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -901,8 +901,8 @@
901901
}
902902
},
903903
"dependencies": {
904-
"@babel/parser": "^7.17.0",
905-
"@babel/traverse": "^7.17.0",
904+
"@babel/parser": "^7.17.3",
905+
"@babel/traverse": "^7.17.3",
906906
"@fortawesome/fontawesome-svg-core": "^1.2.36",
907907
"@fortawesome/free-solid-svg-icons": "^5.15.4",
908908
"@fortawesome/react-fontawesome": "^0.1.17",

src/explorer/helpTree.ts

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

104-
const userId = this._telemetryService?.getSegmentUserId();
105-
const segmentId = userId ? `&ajs_aid=${userId}` : '';
104+
const segmentAnonymousId = this._telemetryService?.getSegmentAnonymousId();
105+
const ajsAid = segmentAnonymousId ? `&ajs_aid=${segmentAnonymousId}` : '';
106106
const atlas = new HelpLinkTreeItem(
107107
'Create Free Atlas Cluster',
108-
`https://mongodb.com/products/vs-code/vs-code-atlas-signup?utm_campaign=vs-code-extension&utm_source=visual-studio&utm_medium=product${segmentId}`,
108+
`https://mongodb.com/products/vs-code/vs-code-atlas-signup?utm_campaign=vs-code-extension&utm_source=visual-studio&utm_medium=product${ajsAid}`,
109109
'freeClusterCTA',
110110
'atlas',
111111
true

src/storage/storageController.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ export enum StorageVariables {
77
// Only exists on globalState.
88
GLOBAL_HAS_BEEN_SHOWN_INITIAL_VIEW = 'GLOBAL_HAS_BEEN_SHOWN_INITIAL_VIEW',
99
GLOBAL_SAVED_CONNECTIONS = 'GLOBAL_SAVED_CONNECTIONS',
10+
// Analytics user identify.
1011
GLOBAL_USER_ID = 'GLOBAL_USER_ID',
12+
GLOBAL_ANONYMOUS_ID = 'GLOBAL_ANONYMOUS_ID',
1113
// Only exists on workspaceState.
1214
WORKSPACE_SAVED_CONNECTIONS = 'WORKSPACE_SAVED_CONNECTIONS'
1315
}
@@ -32,6 +34,7 @@ export type ConnectionsFromStorage = {
3234

3335
interface StorageVariableContents {
3436
[StorageVariables.GLOBAL_USER_ID]: string;
37+
[StorageVariables.GLOBAL_ANONYMOUS_ID]: string;
3538
[StorageVariables.GLOBAL_HAS_BEEN_SHOWN_INITIAL_VIEW]: boolean;
3639
[StorageVariables.GLOBAL_SAVED_CONNECTIONS]: ConnectionsFromStorage;
3740
[StorageVariables.WORKSPACE_SAVED_CONNECTIONS]: ConnectionsFromStorage;
@@ -63,17 +66,35 @@ export default class StorageController {
6366
return Promise.resolve();
6467
}
6568

66-
getUserID(): string {
67-
let globalUserId = this.get(StorageVariables.GLOBAL_USER_ID);
69+
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+
83+
const globalUserId = this.get(StorageVariables.GLOBAL_USER_ID);
6884

6985
if (globalUserId && typeof globalUserId === 'string') {
70-
return globalUserId;
86+
globalAnonymousId = globalUserId;
87+
} else {
88+
globalAnonymousId = uuidv4();
7189
}
7290

73-
globalUserId = uuidv4();
74-
void this.update(StorageVariables.GLOBAL_USER_ID, globalUserId);
91+
void this.update(StorageVariables.GLOBAL_ANONYMOUS_ID, globalAnonymousId);
92+
93+
return globalAnonymousId;
94+
}
7595

76-
return globalUserId;
96+
getUserId(): string | undefined {
97+
return this.get(StorageVariables.GLOBAL_USER_ID);
7798
}
7899

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

src/telemetry/telemetryService.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ 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+
2126
type PlaygroundTelemetryEventProperties = {
2227
type: string | null;
2328
partial: boolean;
@@ -26,7 +31,8 @@ type PlaygroundTelemetryEventProperties = {
2631

2732
export type SegmentProperties = {
2833
event: string;
29-
userId: string;
34+
userId?: string;
35+
anonymousId: string;
3036
properties: unknown;
3137
};
3238

@@ -85,7 +91,8 @@ export enum TelemetryEventTypes {
8591
*/
8692
export default class TelemetryService {
8793
_segmentAnalytics?: SegmentAnalytics;
88-
_segmentUserID: string; // The user uuid from the global storage.
94+
_segmentUserId?: string; // Should exist only for users prior v0.9.0.
95+
_segmentAnonymousId: string; // The randomly generated uuid.
8996
_segmentKey?: string; // The segment API write key.
9097

9198
private _context: vscode.ExtensionContext;
@@ -96,9 +103,11 @@ export default class TelemetryService {
96103
context: vscode.ExtensionContext,
97104
shouldTrackTelemetry?: boolean
98105
) {
106+
const { userId, anonymousId } = storageController.getUserIdentity();
99107
this._context = context;
100108
this._shouldTrackTelemetry = shouldTrackTelemetry || false;
101-
this._segmentUserID = storageController.getUserID();
109+
this._segmentUserId = userId;
110+
this._segmentAnonymousId = anonymousId;
102111
this._segmentKey = this._readSegmentKey();
103112

104113
vscode.workspace.onDidOpenTextDocument((document) => {
@@ -153,7 +162,13 @@ export default class TelemetryService {
153162
flushInterval: 10000 // 10 seconds is the default libraries' value.
154163
});
155164

156-
this._segmentAnalytics.identify({ userId: this._segmentUserID });
165+
const identity: AnalyticsNodeIdentity = {
166+
anonymousId: this._segmentAnonymousId
167+
};
168+
if (this._segmentUserId) {
169+
identity.userId = this._segmentUserId;
170+
}
171+
this._segmentAnalytics.identify(identity);
157172
}
158173
}
159174

@@ -190,7 +205,8 @@ export default class TelemetryService {
190205
if (this._isTelemetryFeatureEnabled()) {
191206
const segmentProperties: SegmentProperties = {
192207
event: eventType,
193-
userId: this._segmentUserID,
208+
userId: this._segmentUserId,
209+
anonymousId: this._segmentAnonymousId,
194210
properties: {
195211
...properties,
196212
extension_version: `${version}`
@@ -260,8 +276,8 @@ export default class TelemetryService {
260276
return 'other';
261277
}
262278

263-
getSegmentUserId(): string {
264-
return this._segmentUserID;
279+
getSegmentAnonymousId(): string {
280+
return this._segmentAnonymousId;
265281
}
266282

267283
trackPlaygroundCodeExecuted(

src/test/suite/connectionController.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ suite('Connection Controller Test Suite', function () {
262262
assert.strictEqual(connections[Object.keys(connections)[2]].connectionOptions?.connectionString, expectedDriverUrl);
263263
});
264264

265-
test('when a connection is added it is saved to the global store', async () => {
265+
test('when a connection is added it is saved to the global storage', async () => {
266266
await testConnectionController.loadSavedConnections();
267267
await vscode.workspace
268268
.getConfiguration('mdb.connectionSaving')
@@ -460,7 +460,7 @@ suite('Connection Controller Test Suite', function () {
460460
assert.strictEqual(Object.keys(postWorkspaceStoreConnections).length, 0);
461461
});
462462

463-
test('when a connection is removed it is also removed from global store', async () => {
463+
test('when a connection is removed it is also removed from global storage', async () => {
464464
await testConnectionController.loadSavedConnections();
465465
await vscode.workspace
466466
.getConfiguration('mdb.connectionSaving')
@@ -951,7 +951,7 @@ suite('Connection Controller Test Suite', function () {
951951
assert.strictEqual(mockMigratePreviouslySavedConnection.called, false);
952952
});
953953

954-
test('addNewConnectionStringAndConnect saves connection without secrets to the global store', async () => {
954+
test('addNewConnectionStringAndConnect saves connection without secrets to the global storage', async () => {
955955
const mockConnect: any = sinon.fake.resolves({ successfullyConnected: true });
956956

957957
sinon.replace(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ suite('Help Explorer Test Suite', function () {
4444
assert.strictEqual(atlasHelpItem.label, 'Create Free Atlas Cluster');
4545
assert.strictEqual(
4646
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.getSegmentUserId()}`
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()}`
4848
);
4949
assert.strictEqual(atlasHelpItem.iconName, 'atlas');
5050
assert.strictEqual(atlasHelpItem.linkId, 'freeClusterCTA');

0 commit comments

Comments
 (0)