Skip to content

Commit 395b42f

Browse files
authored
chore(telemetry): Add extension version to connected event VSCODE-126 (#246)
1 parent 906b080 commit 395b42f

File tree

2 files changed

+147
-27
lines changed

2 files changed

+147
-27
lines changed

src/telemetry/telemetryService.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { DocumentSource } from '../utils/documentSource';
1414
import fs from 'fs';
1515
import * as util from 'util';
1616

17+
const { version } = require('../../package.json');
18+
1719
const log = createLogger('telemetry');
1820

1921
const ATLAS_REGEX = /mongodb.net[:/]/i;
@@ -25,10 +27,10 @@ type PlaygroundTelemetryEventProperties = {
2527
error: boolean;
2628
};
2729

28-
type SegmentProperties = {
30+
export type SegmentProperties = {
2931
event: string;
3032
userId: string;
31-
properties?: any;
33+
properties: any;
3234
};
3335

3436
type CloudInfo = {
@@ -45,7 +47,7 @@ type ExtensionCommandRunTelemetryEventProperties = {
4547
command: string;
4648
};
4749

48-
type NewConnectionTelemetryEventProperties = {
50+
export type NewConnectionTelemetryEventProperties = {
4951
/* eslint-disable camelcase */
5052
is_atlas: boolean;
5153
is_localhost: boolean;
@@ -175,7 +177,7 @@ export default class TelemetryService {
175177

176178
// Checks user settings and extension running mode
177179
// to determine whether or not we should track telemetry.
178-
private isTelemetryFeatureEnabled(): boolean {
180+
_isTelemetryFeatureEnabled(): boolean {
179181
// If tests run the extension we do not track telemetry.
180182
if (this._shouldTrackTelemetry !== true) {
181183
return false;
@@ -198,16 +200,16 @@ export default class TelemetryService {
198200
eventType: TelemetryEventTypes,
199201
properties?: TelemetryEventProperties
200202
): void {
201-
if (this.isTelemetryFeatureEnabled()) {
203+
if (this._isTelemetryFeatureEnabled()) {
202204
const segmentProperties: SegmentProperties = {
203205
event: eventType,
204-
userId: this._segmentUserID
206+
userId: this._segmentUserID,
207+
properties: {
208+
...properties,
209+
extension_version: `${version}`
210+
}
205211
};
206212

207-
if (properties) {
208-
segmentProperties.properties = properties;
209-
}
210-
211213
log.info('TELEMETRY track', segmentProperties);
212214

213215
this._segmentAnalytics?.track(segmentProperties, (error: any) => {
@@ -223,7 +225,7 @@ export default class TelemetryService {
223225
private async getCloudInfoFromDataService(
224226
firstServerHostname: string
225227
): Promise<CloudInfo> {
226-
if (!this.isTelemetryFeatureEnabled()) {
228+
if (!this._isTelemetryFeatureEnabled()) {
227229
return {};
228230
}
229231

@@ -279,7 +281,7 @@ export default class TelemetryService {
279281
const nonGenuineServerName = data.genuineMongoDB.isGenuine
280282
? null
281283
: data.genuineMongoDB.dbType;
282-
const preparedProperties = {
284+
const preparedProperties: NewConnectionTelemetryEventProperties = {
283285
is_atlas: !!ATLAS_REGEX.exec(data.client.s.url),
284286
is_localhost: !!LOCALHOST_REGEX.exec(data.client.s.url),
285287
is_data_lake: data.dataLake.isDataLake,

src/test/suite/telemetry/telemetryController.test.ts renamed to src/test/suite/telemetry/telemetryService.test.ts

Lines changed: 133 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,20 @@ import * as path from 'path';
66
import { resolve } from 'path';
77
import sinon from 'sinon';
88
import sinonChai from 'sinon-chai';
9-
9+
import { promisify } from 'util';
10+
import Sinon = require('sinon');
11+
import DataService = require('mongodb-data-service');
1012
import Connection = require('mongodb-connection-model/lib/model');
13+
1114
import { ConnectionTypes } from '../../../connectionController';
12-
import DataService = require('mongodb-data-service');
15+
import {
16+
NewConnectionTelemetryEventProperties,
17+
SegmentProperties,
18+
TelemetryEventTypes
19+
} from '../../../telemetry/telemetryService';
1320
import { DocumentSource } from '../../../utils/documentSource';
1421
import { mdbTestExtension } from '../stubbableMdbExtension';
22+
const { version } = require('../../../../package.json');
1523

1624
const expect = chai.expect;
1725

@@ -20,8 +28,6 @@ chai.use(sinonChai);
2028
config({ path: resolve(__dirname, '../../../../.env') });
2129

2230
suite('Telemetry Controller Test Suite', () => {
23-
vscode.window.showInformationMessage('Starting tests...');
24-
2531
const dataService = new DataService(
2632
new Connection({
2733
hostname: 'localhost',
@@ -31,11 +37,11 @@ suite('Telemetry Controller Test Suite', () => {
3137
const testTelemetryService =
3238
mdbTestExtension.testExtensionController._telemetryService;
3339

34-
let mockTrackNewConnection: any;
35-
let mockTrackCommandRun: any;
36-
let mockTrackPlaygroundCodeExecuted: any;
37-
let mockTrackPlaygroundLoadedMethod: any;
38-
let mockTrack: any;
40+
let mockTrackNewConnection: Sinon.SinonSpy;
41+
let mockTrackCommandRun: Sinon.SinonSpy;
42+
let mockTrackPlaygroundCodeExecuted: Sinon.SinonSpy;
43+
let mockTrackPlaygroundLoadedMethod: Sinon.SinonSpy;
44+
let mockTrack: Sinon.SinonSpy;
3945

4046
beforeEach(() => {
4147
mockTrackNewConnection = sinon.fake.resolves(true);
@@ -44,11 +50,6 @@ suite('Telemetry Controller Test Suite', () => {
4450
mockTrackPlaygroundLoadedMethod = sinon.fake();
4551
mockTrack = sinon.fake();
4652

47-
sinon.replace(
48-
mdbTestExtension.testExtensionController._telemetryService,
49-
'trackNewConnection',
50-
mockTrackNewConnection
51-
);
5253
sinon.replace(
5354
mdbTestExtension.testExtensionController._telemetryService,
5455
'trackCommandRun',
@@ -69,11 +70,11 @@ suite('Telemetry Controller Test Suite', () => {
6970
'executeAll',
7071
sinon.fake.resolves([{ type: 'TEST', content: 'Result' }])
7172
);
72-
sinon.replace(testTelemetryService, 'track', mockTrack);
7373
});
7474

7575
afterEach(() => {
7676
sinon.restore();
77+
mdbTestExtension.testExtensionController._connectionController.clearAllConnections();
7778
});
7879

7980
test('get segment key and user id', () => {
@@ -104,6 +105,12 @@ suite('Telemetry Controller Test Suite', () => {
104105
const mockConnectionController =
105106
mdbTestExtension.testExtensionController._connectionController;
106107

108+
sinon.replace(
109+
mdbTestExtension.testExtensionController._telemetryService,
110+
'trackNewConnection',
111+
mockTrackNewConnection
112+
);
113+
107114
mockConnectionController.sendTelemetry(
108115
dataService,
109116
ConnectionTypes.CONNECTION_STRING
@@ -120,6 +127,12 @@ suite('Telemetry Controller Test Suite', () => {
120127
const mockConnectionController =
121128
mdbTestExtension.testExtensionController._connectionController;
122129

130+
sinon.replace(
131+
mdbTestExtension.testExtensionController._telemetryService,
132+
'trackNewConnection',
133+
mockTrackNewConnection
134+
);
135+
123136
mockConnectionController.sendTelemetry(
124137
dataService,
125138
ConnectionTypes.CONNECTION_FORM
@@ -136,6 +149,12 @@ suite('Telemetry Controller Test Suite', () => {
136149
const mockConnectionController =
137150
mdbTestExtension.testExtensionController._connectionController;
138151

152+
sinon.replace(
153+
mdbTestExtension.testExtensionController._telemetryService,
154+
'trackNewConnection',
155+
mockTrackNewConnection
156+
);
157+
139158
mockConnectionController.sendTelemetry(
140159
dataService,
141160
ConnectionTypes.CONNECTION_ID
@@ -151,6 +170,8 @@ suite('Telemetry Controller Test Suite', () => {
151170
test('track document saved form a tree-view event', () => {
152171
const source = DocumentSource.DOCUMENT_SOURCE_TREEVIEW;
153172

173+
sinon.replace(testTelemetryService, 'track', mockTrack);
174+
154175
testTelemetryService.trackDocumentUpdated(source, true);
155176

156177
sinon.assert.calledWith(
@@ -203,11 +224,108 @@ suite('Telemetry Controller Test Suite', () => {
203224
});
204225

205226
test('track playground saved event', () => {
227+
sinon.replace(testTelemetryService, 'track', mockTrack);
228+
206229
testTelemetryService.trackPlaygroundSaved();
207230

208231
sinon.assert.calledWith(mockTrack);
209232
});
210233

234+
test('track adds extension version to event properties when there are no event properties', () => {
235+
sinon.replace(
236+
testTelemetryService,
237+
'_isTelemetryFeatureEnabled',
238+
sinon.fake.returns(true)
239+
);
240+
const fakeSegmentTrack = sinon.fake.yields(null);
241+
sinon.replace(
242+
testTelemetryService,
243+
'_segmentAnalytics',
244+
{
245+
track: fakeSegmentTrack
246+
} as any
247+
);
248+
249+
testTelemetryService.track(
250+
TelemetryEventTypes.EXTENSION_LINK_CLICKED
251+
);
252+
253+
const telemetryEvent: SegmentProperties = fakeSegmentTrack.firstCall.args[0];
254+
255+
expect(telemetryEvent.properties).to.deep.equal({
256+
extension_version: version
257+
});
258+
expect(telemetryEvent.event).to.equal('Link Clicked');
259+
});
260+
261+
test('track adds extension version to existing event properties', () => {
262+
sinon.replace(
263+
testTelemetryService,
264+
'_isTelemetryFeatureEnabled',
265+
sinon.fake.returns(true)
266+
);
267+
const fakeSegmentTrack = sinon.fake.yields(null);
268+
sinon.replace(
269+
testTelemetryService,
270+
'_segmentAnalytics',
271+
{
272+
track: fakeSegmentTrack
273+
} as any
274+
);
275+
276+
testTelemetryService.track(
277+
TelemetryEventTypes.PLAYGROUND_LOADED,
278+
{
279+
source: DocumentSource.DOCUMENT_SOURCE_PLAYGROUND
280+
}
281+
);
282+
283+
const telemetryEvent: SegmentProperties = fakeSegmentTrack.firstCall.args[0];
284+
285+
expect(telemetryEvent.properties).to.deep.equal({
286+
extension_version: version,
287+
source: DocumentSource.DOCUMENT_SOURCE_PLAYGROUND
288+
});
289+
expect(telemetryEvent.event).to.equal('Playground Loaded');
290+
});
291+
292+
suite('with active connection', () => {
293+
let dataServ;
294+
295+
beforeEach(async () => {
296+
dataServ = new DataService(
297+
new Connection({
298+
hostname: 'localhost',
299+
port: 27018
300+
})
301+
);
302+
const runConnect = promisify(dataServ.connect.bind(dataServ));
303+
await runConnect();
304+
});
305+
306+
afterEach(async () => {
307+
const runDisconnect = promisify(dataServ.disconnect.bind(dataServ));
308+
await runDisconnect();
309+
});
310+
311+
test('track new connection event fetches the connection instance information', async() => {
312+
sinon.replace(testTelemetryService, 'track', mockTrack);
313+
await mdbTestExtension.testExtensionController._telemetryService.trackNewConnection(
314+
dataServ,
315+
ConnectionTypes.CONNECTION_STRING
316+
);
317+
318+
expect(mockTrack.firstCall.args[0]).to.equal('New Connection');
319+
const instanceTelemetry: NewConnectionTelemetryEventProperties = mockTrack.firstCall.args[1];
320+
expect(instanceTelemetry.is_localhost).to.equal(true);
321+
expect(instanceTelemetry.is_atlas).to.equal(false);
322+
expect(instanceTelemetry.is_used_connect_screen).to.equal(false);
323+
expect(instanceTelemetry.is_used_command_palette).to.equal(true);
324+
expect(instanceTelemetry.is_used_saved_connection).to.equal(false);
325+
expect(instanceTelemetry.is_genuine).to.equal(true);
326+
});
327+
});
328+
211329
suite('prepare playground result types', () => {
212330
test('convert AggregationCursor shellApiType to aggregation telemetry type', () => {
213331
const res = {

0 commit comments

Comments
 (0)