@@ -52,29 +52,35 @@ import type {
52
52
MongoshLoggingAndTelemetryArguments ,
53
53
MongoshTrackingProperties ,
54
54
} from './types' ;
55
- import { machineIdSync } from 'node-machine-id' ;
55
+ import { machineId } from 'node-machine-id' ;
56
+ import { createHmac } from 'crypto' ;
56
57
57
58
export function setupLoggingAndTelemetry (
58
59
props : MongoshLoggingAndTelemetryArguments
59
60
) : MongoshLoggingAndTelemetry {
60
- if ( ! props . deviceId ) {
61
- try {
62
- props . deviceId = machineIdSync ( ) ;
63
- } catch ( error ) {
64
- props . bus . emit (
65
- 'mongosh:error' ,
66
- new Error ( 'Failed to get device ID' ) ,
67
- 'telemetry'
68
- ) ;
69
- }
70
- }
71
-
72
61
const loggingAndTelemetry = new LoggingAndTelemetry ( props ) ;
73
62
74
63
loggingAndTelemetry . setup ( ) ;
75
64
return loggingAndTelemetry ;
76
65
}
77
66
67
+ /**
68
+ * @returns A hashed, unique identifier for the running device.
69
+ * @throws If something goes wrong when getting the device ID.
70
+ */
71
+ export async function getDeviceId ( ) : Promise < string > {
72
+ // Create a hashed format from the all uppercase version of the machine ID
73
+ // to match it exactly with the denisbrodbeck/machineid library that Atlas CLI uses.
74
+ const originalId = ( await machineId ( true ) ) . toUpperCase ( ) ;
75
+ const hmac = createHmac ( 'sha256' , originalId ) ;
76
+
77
+ /** This matches the message used to create the hashes in Atlas CLI */
78
+ const DEVICE_ID_HASH_MESSAGE = 'atlascli' ;
79
+
80
+ hmac . update ( DEVICE_ID_HASH_MESSAGE ) ;
81
+ return hmac . digest ( 'hex' ) ;
82
+ }
83
+
78
84
class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
79
85
private static dummyLogger = new MongoLogWriter (
80
86
'' ,
@@ -170,6 +176,7 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
170
176
usesShellOption : false ,
171
177
telemetryAnonymousId : undefined ,
172
178
userId : undefined ,
179
+ deviceId : undefined ,
173
180
} ;
174
181
175
182
private setupBusEventListeners ( ) : void {
@@ -207,15 +214,47 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
207
214
session_id : this . log . logId ,
208
215
} ) ;
209
216
217
+ /** Eventually sets up the device ID and re-identifies the user. */
218
+ const getCurrentDeviceId = async ( ) : Promise < string > => {
219
+ try {
220
+ this . busEventState . deviceId ??= this . deviceId ?? ( await getDeviceId ( ) ) ;
221
+ } catch ( error ) {
222
+ this . bus . emit ( 'mongosh:error' , error as Error , 'telemetry' ) ;
223
+ this . busEventState . deviceId = 'unknown' ;
224
+ }
225
+ return this . busEventState . deviceId ;
226
+ } ;
227
+
210
228
const getTelemetryUserIdentity = ( ) : MongoshAnalyticsIdentity => {
211
229
return {
230
+ deviceId : this . busEventState . deviceId ?? this . deviceId ,
212
231
anonymousId :
213
232
this . busEventState . telemetryAnonymousId ??
214
233
( this . busEventState . userId as string ) ,
215
- deviceId : this . deviceId ,
216
234
} ;
217
235
} ;
218
236
237
+ const identifyUser = async ( ) : Promise < void > => {
238
+ // We first instantly identify the user with the
239
+ // current user information we have.
240
+ this . analytics . identify ( {
241
+ ...getTelemetryUserIdentity ( ) ,
242
+ traits : getUserTraits ( ) ,
243
+ } ) ;
244
+
245
+ if ( ! this . busEventState . deviceId ) {
246
+ // If the Device ID had not been resolved yet,
247
+ // we wait to resolve it and re-identify the user.
248
+ this . busEventState . deviceId ??= await getCurrentDeviceId ( ) ;
249
+
250
+ this . analytics . identify ( {
251
+ ...getTelemetryUserIdentity ( ) ,
252
+ deviceId : await getCurrentDeviceId ( ) ,
253
+ traits : getUserTraits ( ) ,
254
+ } ) ;
255
+ }
256
+ } ;
257
+
219
258
onBus ( 'mongosh:start-mongosh-repl' , ( ev : StartMongoshReplEvent ) => {
220
259
this . log . info (
221
260
'MONGOSH' ,
@@ -301,10 +340,8 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
301
340
}
302
341
this . busEventState . telemetryAnonymousId =
303
342
newTelemetryUserIdentity . anonymousId ;
304
- this . analytics . identify ( {
305
- ...getTelemetryUserIdentity ( ) ,
306
- traits : getUserTraits ( ) ,
307
- } ) ;
343
+
344
+ void identifyUser ( ) ;
308
345
}
309
346
) ;
310
347
@@ -320,10 +357,7 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
320
357
} else {
321
358
this . busEventState . userId = updatedTelemetryUserIdentity . userId ;
322
359
}
323
- this . analytics . identify ( {
324
- ...getTelemetryUserIdentity ( ) ,
325
- traits : getUserTraits ( ) ,
326
- } ) ;
360
+ void identifyUser ( ) ;
327
361
this . log . info (
328
362
'MONGOSH' ,
329
363
mongoLogId ( 1_000_000_005 ) ,
0 commit comments