-
Notifications
You must be signed in to change notification settings - Fork 132
feat: update connectionString appName param - [MCP-68] #406
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
Changes from 10 commits
c5c91e9
4cf78c2
026b91a
680e1e1
92aab61
eca40e1
6cf0ae6
524d965
72f1ab8
c3f0928
f8de877
8048cf6
0c620b2
d148376
13a0349
bb97041
5fb0284
a690850
a2e1091
f964e12
6e922e6
4ba3a16
78d430a
ae1d6d0
68a462e
720be15
565ca2b
69609b4
bdf1272
658cdc8
dae4f06
858ce1e
0bc1a9f
b0972bd
e570562
8e62d2e
0ed8d23
abf285a
e7727ee
f9c22d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,51 @@ | ||
import { MongoClientOptions } from "mongodb"; | ||
import ConnectionString from "mongodb-connection-string-url"; | ||
import { getDeviceIdForConnection } from "./deviceId.js"; | ||
|
||
export function setAppNameParamIfMissing({ | ||
export interface AppNameComponents { | ||
appName: string; | ||
deviceId?: string; | ||
clientName?: string; | ||
} | ||
|
||
/** | ||
* Sets the appName parameter with the extended format: appName--deviceId--clientName | ||
* Only sets the appName if it's not already present in the connection string | ||
* @param connectionString - The connection string to modify | ||
* @param components - The components to build the appName from | ||
* @returns The modified connection string | ||
*/ | ||
export async function setAppNameParamIfMissing({ | ||
connectionString, | ||
defaultAppName, | ||
components, | ||
}: { | ||
connectionString: string; | ||
defaultAppName?: string; | ||
}): string { | ||
components: AppNameComponents; | ||
}): Promise<string> { | ||
const connectionStringUrl = new ConnectionString(connectionString); | ||
|
||
const searchParams = connectionStringUrl.typedSearchParams<MongoClientOptions>(); | ||
|
||
if (!searchParams.has("appName") && defaultAppName !== undefined) { | ||
searchParams.set("appName", defaultAppName); | ||
// Only set appName if it's not already present | ||
if (searchParams.has("appName")) { | ||
return connectionStringUrl.toString(); | ||
} | ||
|
||
// Get deviceId if not provided | ||
let deviceId = components.deviceId; | ||
blva marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
if (!deviceId) { | ||
deviceId = await getDeviceIdForConnection(); | ||
} | ||
|
||
// Get clientName if not provided | ||
let clientName = components.clientName; | ||
blva marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
if (!clientName) { | ||
clientName = "unknown"; | ||
} | ||
|
||
// Build the extended appName format: appName--deviceId--clientName | ||
const extendedAppName = `${components.appName}--${deviceId}--${clientName}`; | ||
blva marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
searchParams.set("appName", extendedAppName); | ||
|
||
return connectionStringUrl.toString(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { getDeviceId } from "@mongodb-js/device-id"; | ||
import nodeMachineId from "node-machine-id"; | ||
import logger, { LogId } from "../common/logger.js"; | ||
|
||
export const DEVICE_ID_TIMEOUT = 3000; | ||
gagik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** | ||
* Retrieves the device ID for telemetry purposes. | ||
* The device ID is generated using the machine ID and additional logic to handle errors. | ||
* | ||
* @returns Promise that resolves to the device ID string | ||
* If an error occurs during retrieval, the function returns "unknown". | ||
* | ||
* @example | ||
* ```typescript | ||
* const deviceId = await getDeviceIdForConnection(); | ||
* console.log(deviceId); // Outputs the device ID or "unknown" in case of failure | ||
* ``` | ||
*/ | ||
export async function getDeviceIdForConnection(): Promise<string> { | ||
try { | ||
const deviceId = await getDeviceId({ | ||
getMachineId: () => nodeMachineId.machineId(true), | ||
onError: (reason, error) => { | ||
switch (reason) { | ||
case "resolutionError": | ||
logger.debug(LogId.telemetryDeviceIdFailure, "deviceId", String(error)); | ||
break; | ||
case "timeout": | ||
logger.debug(LogId.telemetryDeviceIdTimeout, "deviceId", "Device ID retrieval timed out"); | ||
break; | ||
case "abort": | ||
// No need to log in the case of aborts | ||
break; | ||
} | ||
}, | ||
abortSignal: new AbortController().signal, | ||
blva marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
}); | ||
return deviceId; | ||
} catch (error) { | ||
logger.debug(LogId.telemetryDeviceIdFailure, "deviceId", `Failed to get device ID: ${String(error)}`); | ||
return "unknown"; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,33 +5,27 @@ import logger, { LogId } from "../common/logger.js"; | |
import { ApiClient } from "../common/atlas/apiClient.js"; | ||
import { MACHINE_METADATA } from "./constants.js"; | ||
import { EventCache } from "./eventCache.js"; | ||
import nodeMachineId from "node-machine-id"; | ||
import { getDeviceId } from "@mongodb-js/device-id"; | ||
import { getDeviceIdForConnection } from "../helpers/deviceId.js"; | ||
import { detectContainerEnv } from "../helpers/container.js"; | ||
|
||
type EventResult = { | ||
success: boolean; | ||
error?: Error; | ||
}; | ||
|
||
export const DEVICE_ID_TIMEOUT = 3000; | ||
|
||
export class Telemetry { | ||
private isBufferingEvents: boolean = true; | ||
/** Resolves when the setup is complete or a timeout occurs */ | ||
public setupPromise: Promise<[string, boolean]> | undefined; | ||
private deviceIdAbortController = new AbortController(); | ||
private eventCache: EventCache; | ||
private getRawMachineId: () => Promise<string>; | ||
|
||
private constructor( | ||
private readonly session: Session, | ||
private readonly userConfig: UserConfig, | ||
private readonly commonProperties: CommonProperties, | ||
{ eventCache, getRawMachineId }: { eventCache: EventCache; getRawMachineId: () => Promise<string> } | ||
{ eventCache }: { eventCache: EventCache } | ||
|
||
) { | ||
this.eventCache = eventCache; | ||
this.getRawMachineId = getRawMachineId; | ||
} | ||
|
||
static create( | ||
|
@@ -40,14 +34,12 @@ export class Telemetry { | |
{ | ||
commonProperties = { ...MACHINE_METADATA }, | ||
eventCache = EventCache.getInstance(), | ||
getRawMachineId = () => nodeMachineId.machineId(true), | ||
}: { | ||
eventCache?: EventCache; | ||
getRawMachineId?: () => Promise<string>; | ||
commonProperties?: CommonProperties; | ||
} = {} | ||
): Telemetry { | ||
const instance = new Telemetry(session, userConfig, commonProperties, { eventCache, getRawMachineId }); | ||
const instance = new Telemetry(session, userConfig, commonProperties, { eventCache }); | ||
|
||
void instance.setup(); | ||
return instance; | ||
|
@@ -57,26 +49,7 @@ export class Telemetry { | |
if (!this.isTelemetryEnabled()) { | ||
return; | ||
} | ||
this.setupPromise = Promise.all([ | ||
getDeviceId({ | ||
getMachineId: () => this.getRawMachineId(), | ||
onError: (reason, error) => { | ||
switch (reason) { | ||
case "resolutionError": | ||
logger.debug(LogId.telemetryDeviceIdFailure, "telemetry", String(error)); | ||
break; | ||
case "timeout": | ||
logger.debug(LogId.telemetryDeviceIdTimeout, "telemetry", "Device ID retrieval timed out"); | ||
break; | ||
case "abort": | ||
// No need to log in the case of aborts | ||
break; | ||
} | ||
}, | ||
abortSignal: this.deviceIdAbortController.signal, | ||
}), | ||
detectContainerEnv(), | ||
]); | ||
this.setupPromise = Promise.all([getDeviceIdForConnection(), detectContainerEnv()]); | ||
|
||
const [deviceId, containerEnv] = await this.setupPromise; | ||
|
||
|
@@ -87,7 +60,6 @@ export class Telemetry { | |
} | ||
|
||
public async close(): Promise<void> { | ||
this.deviceIdAbortController.abort(); | ||
this.isBufferingEvents = false; | ||
blva marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I moved it to index.ts so I'm closing it there as well, makes sense? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. now moved to transport |
||
await this.emitEvents(this.eventCache.getEvents()); | ||
blva marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.