Skip to content
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