@@ -3,9 +3,6 @@ import * as vscode from 'vscode';
3
3
import Connection = require( 'mongodb-connection-model/lib/model' ) ;
4
4
import DataService = require( 'mongodb-data-service' ) ;
5
5
import * as keytarType from 'keytar' ;
6
-
7
- const { name, version } = require ( '../package.json' ) ;
8
-
9
6
import { ConnectionModelType } from './connectionModelType' ;
10
7
import { DataServiceType } from './dataServiceType' ;
11
8
import { createLogger } from './logging' ;
@@ -14,16 +11,29 @@ import { EventEmitter } from 'events';
14
11
import { StorageController , StorageVariables } from './storage' ;
15
12
import { SavedConnection , StorageScope } from './storage/storageController' ;
16
13
import { getNodeModule } from './utils/getNodeModule' ;
14
+ import TelemetryController , {
15
+ TelemetryEventTypes
16
+ } from './telemetry/telemetryController' ;
17
+ import { getCloudInfo } from 'mongodb-cloud-info' ;
17
18
19
+ const { name, version } = require ( '../package.json' ) ;
18
20
const log = createLogger ( 'connection controller' ) ;
19
21
const MAX_CONNECTION_NAME_LENGTH = 512 ;
22
+ const ATLAS_REGEX = / m o n g o d b .n e t [: / ] / i;
23
+ const LOCALHOST_REGEX = / ( l o c a l h o s t | 1 2 7 \. 0 \. 0 \. 1 ) / i;
20
24
21
25
type KeyTar = typeof keytarType ;
22
26
23
27
export enum DataServiceEventTypes {
24
28
CONNECTIONS_DID_CHANGE = 'CONNECTIONS_DID_CHANGE' ,
25
29
ACTIVE_CONNECTION_CHANGED = 'ACTIVE_CONNECTION_CHANGED' ,
26
- ACTIVE_CONNECTION_CHANGING = 'ACTIVE_CONNECTION_CHANGING' ,
30
+ ACTIVE_CONNECTION_CHANGING = 'ACTIVE_CONNECTION_CHANGING'
31
+ }
32
+
33
+ export enum ConnectionTypes {
34
+ CONNECTION_FORM = 'CONNECTION_FORM' ,
35
+ CONNECTION_STRING = 'CONNECTION_STRING' ,
36
+ CONNECTION_ID = 'CONNECTION_ID'
27
37
}
28
38
29
39
export type SavedConnectionInformation = {
@@ -55,13 +65,19 @@ export default class ConnectionController {
55
65
56
66
private _statusView : StatusView ;
57
67
private _storageController : StorageController ;
68
+ public _telemetryController ?: TelemetryController ;
58
69
59
70
// Used by other parts of the extension that respond to changes in the connections.
60
71
private eventEmitter : EventEmitter = new EventEmitter ( ) ;
61
72
62
- constructor ( _statusView : StatusView , storageController : StorageController ) {
73
+ constructor (
74
+ _statusView : StatusView ,
75
+ storageController : StorageController ,
76
+ telemetryController ?: TelemetryController
77
+ ) {
63
78
this . _statusView = _statusView ;
64
79
this . _storageController = storageController ;
80
+ this . _telemetryController = telemetryController ;
65
81
66
82
try {
67
83
// We load keytar in two different ways. This is because when the
@@ -216,21 +232,21 @@ export default class ConnectionController {
216
232
return reject ( new Error ( `Unable to create connection: ${ error } ` ) ) ;
217
233
}
218
234
219
- return this . saveNewConnectionAndConnect ( newConnectionModel ) . then (
220
- resolve ,
221
- reject
222
- ) ;
235
+ return this . saveNewConnectionAndConnect (
236
+ newConnectionModel ,
237
+ ConnectionTypes . CONNECTION_STRING
238
+ ) . then ( resolve , reject ) ;
223
239
}
224
240
) ;
225
241
} ) ;
226
242
} ;
227
243
228
244
public parseNewConnectionAndConnect = (
229
- newConnectionModel
245
+ newConnectionModel : ConnectionModelType
230
246
) : Promise < boolean > => {
231
247
// Here we re-parse the connection, as it can be loaded from storage or
232
248
// passed by the connection model without the class methods.
233
- let connectionModel ;
249
+ let connectionModel : ConnectionModelType ;
234
250
235
251
try {
236
252
connectionModel = new Connection ( newConnectionModel ) ;
@@ -239,11 +255,15 @@ export default class ConnectionController {
239
255
return Promise . reject ( new Error ( `Unable to load connection: ${ error } ` ) ) ;
240
256
}
241
257
242
- return this . saveNewConnectionAndConnect ( connectionModel ) ;
258
+ return this . saveNewConnectionAndConnect (
259
+ connectionModel ,
260
+ ConnectionTypes . CONNECTION_FORM
261
+ ) ;
243
262
} ;
244
263
245
264
public saveNewConnectionAndConnect = async (
246
- connectionModel : ConnectionModelType
265
+ connectionModel : ConnectionModelType ,
266
+ connectionType : ConnectionTypes
247
267
) : Promise < boolean > => {
248
268
const { driverUrl, instanceId } = connectionModel . getAttributes ( {
249
269
derived : true
@@ -279,19 +299,91 @@ export default class ConnectionController {
279
299
}
280
300
281
301
return new Promise ( ( resolve , reject ) => {
282
- this . connect ( connectionId , connectionModel ) . then ( ( connectSuccess ) => {
283
- if ( ! connectSuccess ) {
284
- return resolve ( false ) ;
285
- }
302
+ this . connect ( connectionId , connectionModel , connectionType ) . then (
303
+ ( connectSuccess ) => {
304
+ if ( ! connectSuccess ) {
305
+ return resolve ( false ) ;
306
+ }
286
307
287
- resolve ( true ) ;
288
- } , reject ) ;
308
+ resolve ( true ) ;
309
+ } ,
310
+ reject
311
+ ) ;
289
312
} ) ;
290
313
} ;
291
314
315
+ public async getCloudInfoFromDataService ( firstServerHostname ) {
316
+ const cloudInfo = await getCloudInfo ( firstServerHostname ) ;
317
+ let isPublicCloud = false ;
318
+ let publicCloudName : string | null = null ;
319
+
320
+ if ( cloudInfo . isAws ) {
321
+ isPublicCloud = true ;
322
+ publicCloudName = 'aws' ;
323
+ } else if ( cloudInfo . isGcp ) {
324
+ isPublicCloud = true ;
325
+ publicCloudName = 'gcp' ;
326
+ } else if ( cloudInfo . isAzure ) {
327
+ isPublicCloud = true ;
328
+ publicCloudName = 'azure' ;
329
+ }
330
+
331
+ return { isPublicCloud, publicCloudName } ;
332
+ }
333
+
334
+ private async sendTelemetry (
335
+ dataService : DataServiceType ,
336
+ connectionType : ConnectionTypes
337
+ ) : Promise < void > {
338
+ dataService . instance ( { } , async ( error : any , data : any ) => {
339
+ if ( error ) {
340
+ log . error ( 'TELEMETRY data service error' , error ) ;
341
+ }
342
+ if ( data ) {
343
+ try {
344
+ const firstServerHostname = dataService . client . model . hosts [ 0 ] . host ;
345
+ const cloudInfo = await this . getCloudInfoFromDataService (
346
+ firstServerHostname
347
+ ) ;
348
+ const nonGenuineServerName = data . genuineMongoDB . isGenuine
349
+ ? null
350
+ : data . genuineMongoDB . dbType ;
351
+ const telemetryData = {
352
+ isAtlas : ! ! data . client . s . url . match ( ATLAS_REGEX ) ,
353
+ isLocalhost : ! ! data . client . s . url . match ( LOCALHOST_REGEX ) ,
354
+ isDataLake : data . dataLake . isDataLake ,
355
+ isEnterprise : data . build . enterprise_module ,
356
+ isPublicCloud : cloudInfo . isPublicCloud ,
357
+ publicCloudName : cloudInfo . publicCloudName ,
358
+ isGenuine : data . genuineMongoDB . isGenuine ,
359
+ nonGenuineServerName,
360
+ serverVersion : data . build . version ,
361
+ serverArch : data . build . raw . buildEnvironment . target_arch ,
362
+ serverOS : data . build . raw . buildEnvironment . target_os ,
363
+ isUsedConnectScreen :
364
+ connectionType === ConnectionTypes . CONNECTION_FORM ,
365
+ isUsedCommandPalette :
366
+ connectionType === ConnectionTypes . CONNECTION_STRING ,
367
+ isUsedSavedConnection :
368
+ connectionType === ConnectionTypes . CONNECTION_ID
369
+ } ;
370
+
371
+ // Send metrics to Segment
372
+ this . _telemetryController ?. track (
373
+ TelemetryEventTypes . NEW_CONNECTION ,
374
+ telemetryData
375
+ ) ;
376
+ } catch ( error ) {
377
+ log . error ( 'TELEMETRY cloud info error' , error ) ;
378
+ }
379
+ }
380
+ } ) ;
381
+ }
382
+
292
383
public connect = async (
293
384
connectionId : string ,
294
- connectionModel : ConnectionModelType
385
+ connectionModel : ConnectionModelType ,
386
+ connectionType : ConnectionTypes
295
387
) : Promise < boolean > => {
296
388
log . info (
297
389
'Connect called to connect to instance:' ,
@@ -327,6 +419,7 @@ export default class ConnectionController {
327
419
connectionModel . appname = `${ name } ${ version } ` ;
328
420
329
421
const newDataService : DataServiceType = new DataService ( connectionModel ) ;
422
+
330
423
newDataService . connect ( ( err : Error | undefined ) => {
331
424
this . _statusView . hideMessage ( ) ;
332
425
@@ -348,6 +441,10 @@ export default class ConnectionController {
348
441
this . eventEmitter . emit ( DataServiceEventTypes . CONNECTIONS_DID_CHANGE ) ;
349
442
this . eventEmitter . emit ( DataServiceEventTypes . ACTIVE_CONNECTION_CHANGED ) ;
350
443
444
+ if ( this . _telemetryController ) {
445
+ this . sendTelemetry ( newDataService , connectionType ) ;
446
+ }
447
+
351
448
return resolve ( true ) ;
352
449
} ) ;
353
450
} ) ;
@@ -372,13 +469,14 @@ export default class ConnectionController {
372
469
return Promise . resolve ( false ) ;
373
470
}
374
471
return new Promise ( ( resolve ) => {
375
- this . connect ( connectionId , connectionModel ) . then (
376
- resolve ,
377
- ( err : Error ) => {
378
- vscode . window . showErrorMessage ( err . message ) ;
379
- return resolve ( false ) ;
380
- }
381
- ) ;
472
+ this . connect (
473
+ connectionId ,
474
+ connectionModel ,
475
+ ConnectionTypes . CONNECTION_ID
476
+ ) . then ( resolve , ( err : Error ) => {
477
+ vscode . window . showErrorMessage ( err . message ) ;
478
+ return resolve ( false ) ;
479
+ } ) ;
382
480
} ) ;
383
481
}
384
482
@@ -536,13 +634,13 @@ export default class ConnectionController {
536
634
const connectionNameToRemove :
537
635
| string
538
636
| undefined = await vscode . window . showQuickPick (
539
- connectionIds . map (
540
- ( id , index ) => `${ index + 1 } : ${ this . _connections [ id ] . name } `
541
- ) ,
542
- {
543
- placeHolder : 'Choose a connection to remove...'
544
- }
545
- ) ;
637
+ connectionIds . map (
638
+ ( id , index ) => `${ index + 1 } : ${ this . _connections [ id ] . name } `
639
+ ) ,
640
+ {
641
+ placeHolder : 'Choose a connection to remove...'
642
+ }
643
+ ) ;
546
644
547
645
if ( ! connectionNameToRemove ) {
548
646
return Promise . resolve ( false ) ;
0 commit comments