Skip to content

Commit 9175e66

Browse files
committed
feat: add device ID to compass telemetry
1 parent fc50faf commit 9175e66

File tree

3 files changed

+48
-0
lines changed

3 files changed

+48
-0
lines changed

packages/compass-e2e-tests/tests/logging.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ describe('Logging and Telemetry integration', function () {
179179
expect(actual.hasAnalytics).to.equal(true);
180180
expect(actual.currentUserId).to.not.exist;
181181
expect(actual.telemetryAnonymousId).to.be.a('string');
182+
expect(actual.telemetryDeviceId).to.match(/^[a-f0-9]{64}$/);
182183
expect(actual.state).to.equal('enabled');
183184
},
184185
},
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { createHmac } from 'crypto';
2+
3+
/**
4+
* TODO: This hashing function should eventually be moved to devtools-shared.
5+
* @returns A hashed, unique identifier for the running device or `"unknown"` if not known.
6+
*/
7+
export async function getDeviceId({
8+
onError,
9+
}: {
10+
onError?: (error: Error) => void;
11+
} = {}): Promise<string | 'unknown'> {
12+
try {
13+
// Create a hashed format from the all uppercase version of the machine ID
14+
// to match it exactly with the denisbrodbeck/machineid library that Atlas CLI uses.
15+
const originalId: string =
16+
// eslint-disable-next-line @typescript-eslint/no-var-requires
17+
await require('native-machine-id').getMachineId({
18+
raw: true,
19+
});
20+
21+
if (!originalId) {
22+
return 'unknown';
23+
}
24+
const hmac = createHmac('sha256', originalId);
25+
26+
/** This matches the message used to create the hashes in Atlas CLI */
27+
const DEVICE_ID_HASH_MESSAGE = 'atlascli';
28+
29+
hmac.update(DEVICE_ID_HASH_MESSAGE);
30+
return hmac.digest('hex');
31+
} catch (error) {
32+
onError?.(error as Error);
33+
return 'unknown';
34+
}
35+
}

packages/compass/src/main/telemetry.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { CompassApplication } from './application';
66
import type { EventEmitter } from 'events';
77
import { getOsInfo } from '@mongodb-js/get-os-info';
88
import type { IdentifyTraits } from '@mongodb-js/compass-telemetry';
9+
import { getDeviceId } from './device-id';
910

1011
const { log, mongoLogId } = createLogger('COMPASS-TELEMETRY');
1112

@@ -38,6 +39,7 @@ class CompassTelemetry {
3839
private static queuedEvents: EventInfo[] = []; // Events that happen before we fetch user preferences
3940
private static telemetryAnonymousId = ''; // The randomly generated anonymous user id.
4041
private static telemetryAtlasUserId?: string;
42+
private static telemetryDeviceId = 'unknown';
4143
private static lastReportedScreen = '';
4244
private static osInfo: ReturnType<typeof getOsInfo> extends Promise<infer T>
4345
? Partial<T>
@@ -53,6 +55,7 @@ class CompassTelemetry {
5355
// Used in both track and identify to add common traits
5456
// to any event that we send to segment
5557
return {
58+
device_id: this.telemetryDeviceId,
5659
compass_version: app.getVersion().split('.').slice(0, 2).join('.'),
5760
compass_full_version: app.getVersion(),
5861
compass_distribution: process.env.HADRON_DISTRIBUTION,
@@ -138,6 +141,15 @@ class CompassTelemetry {
138141
preferences.getPreferences();
139142
this.telemetryAnonymousId = telemetryAnonymousId ?? '';
140143
this.telemetryAtlasUserId = telemetryAtlasUserId;
144+
this.telemetryDeviceId = await getDeviceId({
145+
onError: (err) =>
146+
log.error(
147+
mongoLogId(1_001_000_148),
148+
'Telemetry',
149+
'Failed to get device ID',
150+
{ err: err.message }
151+
),
152+
});
141153

142154
try {
143155
this.osInfo = await getOsInfo();

0 commit comments

Comments
 (0)