Skip to content

chore: switch to @mongodb-js/device-id #196

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 8, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default defineConfig([
"coverage",
"global.d.ts",
"eslint.config.js",
"jest.config.ts",
"jest.config.cjs",
]),
eslintPluginPrettierRecommended,
]);
2 changes: 1 addition & 1 deletion jest.config.ts → jest.config.cjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** @type {import('ts-jest').JestConfigWithTsJest} **/
export default {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran into jestjs/jest#15312, possibly because of a TypeScript update, so just going to keep it as a .cjs file for the time being

module.exports = {
preset: "ts-jest/presets/default-esm",
testEnvironment: "node",
extensionsToTreatAsEsm: [".ts"],
Expand Down
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.8.0",
"@mongodb-js/device-id": "^0.2.0",
"@mongodb-js/devtools-connect": "^3.7.2",
"@mongosh/service-provider-node-driver": "^3.6.0",
"bson": "^6.10.3",
Expand Down
58 changes: 0 additions & 58 deletions src/deferred-promise.ts

This file was deleted.

49 changes: 13 additions & 36 deletions src/telemetry/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import logger, { LogId } from "../logger.js";
import { ApiClient } from "../common/atlas/apiClient.js";
import { MACHINE_METADATA } from "./constants.js";
import { EventCache } from "./eventCache.js";
import { createHmac } from "crypto";
import nodeMachineId from "node-machine-id";
import { DeferredPromise } from "../deferred-promise.js";
import { getDeviceId } from "@mongodb-js/device-id";

type EventResult = {
success: boolean;
Expand All @@ -19,7 +18,8 @@ export const DEVICE_ID_TIMEOUT = 3000;
export class Telemetry {
private isBufferingEvents: boolean = true;
/** Resolves when the device ID is retrieved or timeout occurs */
public deviceIdPromise: DeferredPromise<string> | undefined;
public deviceIdPromise: Promise<string> | undefined;
public resolveDeviceId: ((value: string) => void) | undefined;
private eventCache: EventCache;
private getRawMachineId: () => Promise<string>;

Expand All @@ -39,8 +39,6 @@ export class Telemetry {
{
commonProperties = { ...MACHINE_METADATA },
eventCache = EventCache.getInstance(),

// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
getRawMachineId = () => nodeMachineId.machineId(true),
}: {
eventCache?: EventCache;
Expand All @@ -58,50 +56,29 @@ export class Telemetry {
if (!this.isTelemetryEnabled()) {
return;
}
this.deviceIdPromise = DeferredPromise.fromPromise(this.getDeviceId(), {
timeout: DEVICE_ID_TIMEOUT,
const { value: deviceId, resolve: resolveDeviceId } = getDeviceId({
getMachineId: () => this.getRawMachineId(),
isNodeMachineId: true,
onError: (error) => logger.debug(LogId.telemetryDeviceIdFailure, "telemetry", String(error)),
onTimeout: (resolve) => {
resolve("unknown");
logger.debug(LogId.telemetryDeviceIdTimeout, "telemetry", "Device ID retrieval timed out");
resolve("unknown");
},
});
this.commonProperties.device_id = await this.deviceIdPromise;

this.deviceIdPromise = deviceId;
this.resolveDeviceId = resolveDeviceId;
this.commonProperties.device_id = await deviceId;

this.isBufferingEvents = false;
}

public async close(): Promise<void> {
this.deviceIdPromise?.resolve("unknown");
this.resolveDeviceId?.("unknown");
this.isBufferingEvents = false;
await this.emitEvents(this.eventCache.getEvents());
}

/**
* @returns A hashed, unique identifier for the running device or `"unknown"` if not known.
*/
private async getDeviceId(): Promise<string> {
try {
if (this.commonProperties.device_id) {
return this.commonProperties.device_id;
}

const originalId: string = await this.getRawMachineId();

// Create a hashed format from the all uppercase version of the machine ID
// to match it exactly with the denisbrodbeck/machineid library that Atlas CLI uses.
const hmac = createHmac("sha256", originalId.toUpperCase());

/** This matches the message used to create the hashes in Atlas CLI */
const DEVICE_ID_HASH_MESSAGE = "atlascli";

hmac.update(DEVICE_ID_HASH_MESSAGE);
return hmac.digest("hex");
} catch (error) {
logger.debug(LogId.telemetryDeviceIdFailure, "telemetry", String(error));
return "unknown";
}
}

/**
* Emits events through the telemetry pipeline
* @param events - The events to emit
Expand Down
1 change: 0 additions & 1 deletion tests/integration/telemetry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import nodeMachineId from "node-machine-id";

describe("Telemetry", () => {
it("should resolve the actual machine ID", async () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
const actualId: string = await nodeMachineId.machineId(true);

const actualHashedId = createHmac("sha256", actualId.toUpperCase()).update("atlascli").digest("hex");
Expand Down
72 changes: 0 additions & 72 deletions tests/unit/deferred-promise.test.ts

This file was deleted.

Loading