diff --git a/packages/cli-repl/README.md b/packages/cli-repl/README.md index 3e5e23014f..8900f5ff7e 100644 --- a/packages/cli-repl/README.md +++ b/packages/cli-repl/README.md @@ -99,8 +99,8 @@ bus.emit('mongosh:connect', { }) ``` -### bus.on('mongosh:new-user', userID, enableTelemetry) -Where `userID` is a [BSON ObjectID][object-id] and `enableTelemetry` is a boolean flag. +### bus.on('mongosh:new-user', telemetryAnonymousId, enableTelemetry) +Where `telemetryAnonymousId` is a [BSON ObjectID][object-id] and `enableTelemetry` is a boolean flag. This is used for telemetry tracking when the user initially uses mongosh. Example: @@ -108,14 +108,16 @@ Example: bus.emit('mongosh:new-user', '12394dfjvnaw3uw3erdf', true) ``` -### bus.on('mongosh:update-user', id, enableTelemetry) -Where `userID` is a [BSON ObjectID][object-id] and `enableTelemetry` is a boolean flag. -This is used internally to update telemetry preferences and userID in the +### bus.on('mongosh:update-user', telemetryUserIdentity, enableTelemetry) +Initially, we used `userId` as Segment user identifier, but this usage is being deprecated. +The `anonymousId` should be used instead. We keep sending `userId` to Segment for old users though to preserve their analytics. +Where `userID`/`anonymousId` is a [BSON ObjectID][object-id] and `enableTelemetry` is a boolean flag. +This is used internally to update telemetry preferences and `userID`/`anonymousId` in the logger. Example: ```js -bus.emit('mongosh:update-user', '12394dfjvnaw3uw3erdf', false) +bus.emit('mongosh:update-user', { userId: undefined, anonymousId: '12394dfjvnaw3uw3erdf' } , false) ``` ### bus.on('mongosh:error', error) diff --git a/packages/cli-repl/src/cli-repl.spec.ts b/packages/cli-repl/src/cli-repl.spec.ts index a475c2abaf..48967f43c9 100644 --- a/packages/cli-repl/src/cli-repl.spec.ts +++ b/packages/cli-repl/src/cli-repl.spec.ts @@ -102,8 +102,8 @@ describe('CliRepl', () => { const updateUser = waitBus(cliRepl.bus, 'mongosh:update-user'); const evalComplete = waitBus(cliRepl.bus, 'mongosh:eval-complete'); input.write('disableTelemetry()\n'); - const [ userId ] = await updateUser; - expect(typeof userId).to.equal('string'); + const [ telemetryUserIdentity ] = await updateUser; + expect(typeof telemetryUserIdentity).to.equal('object'); await evalComplete; // eval-complete includes the fs.writeFile() call. const content = await fs.readFile(path.join(tmpdir.path, 'config'), { encoding: 'utf8' }); @@ -113,7 +113,7 @@ describe('CliRepl', () => { it('does not store config options on disk that have not been changed', async() => { let content = await fs.readFile(path.join(tmpdir.path, 'config'), { encoding: 'utf8' }); expect(Object.keys(EJSON.parse(content))).to.deep.equal([ - 'userId', 'enableTelemetry', 'disableGreetingMessage' + 'telemetryAnonymousId', 'enableTelemetry', 'disableGreetingMessage' ]); input.write('config.set("inspectDepth", config.get("inspectDepth"))\n'); @@ -121,7 +121,7 @@ describe('CliRepl', () => { await waitEval(cliRepl.bus); content = await fs.readFile(path.join(tmpdir.path, 'config'), { encoding: 'utf8' }); expect(Object.keys(EJSON.parse(content))).to.deep.equal([ - 'userId', 'enableTelemetry', 'disableGreetingMessage', 'inspectDepth' + 'telemetryAnonymousId', 'enableTelemetry', 'disableGreetingMessage', 'inspectDepth' ]); // When a new REPL is created: @@ -129,7 +129,7 @@ describe('CliRepl', () => { await cliRepl.start('', {}); content = await fs.readFile(path.join(tmpdir.path, 'config'), { encoding: 'utf8' }); expect(Object.keys(EJSON.parse(content))).to.deep.equal([ - 'userId', 'enableTelemetry', 'disableGreetingMessage', 'inspectDepth' + 'telemetryAnonymousId', 'enableTelemetry', 'disableGreetingMessage', 'inspectDepth' ]); }); @@ -225,12 +225,12 @@ describe('CliRepl', () => { it('fails when trying to overwrite mongosh-owned config settings', async() => { output = ''; - input.write('config.set("userId", "foo")\n'); + input.write('config.set("telemetryAnonymousId", "foo")\n'); await waitEval(cliRepl.bus); - expect(output).to.include('Option "userId" is not available in this environment'); + expect(output).to.include('Option "telemetryAnonymousId" is not available in this environment'); output = ''; - input.write('config.get("userId")\n'); + input.write('config.get("telemetryAnonymousId")\n'); await waitEval(cliRepl.bus); expect(output).to.match(/^[a-z0-9]{24}\n> $/); }); @@ -286,16 +286,16 @@ describe('CliRepl', () => { }); context('during startup', () => { - it('persists userId', async() => { - const userIds: string[] = []; + it('persists telemetryAnonymousId', async() => { + const telemetryAnonymousIds: string[] = []; for (let i = 0; i < 2; i++) { cliRepl = new CliRepl(cliReplOptions); - cliRepl.bus.on('mongosh:new-user', userId => userIds.push(userId)); - cliRepl.bus.on('mongosh:update-user', userId => userIds.push(userId)); + cliRepl.bus.on('mongosh:new-user', telemetryAnonymousId => telemetryAnonymousIds.push(telemetryAnonymousId)); + cliRepl.bus.on('mongosh:update-user', telemetryUserIdentity => telemetryAnonymousIds.push(telemetryUserIdentity.anonymousId)); await cliRepl.start('', {}); } - expect(userIds).to.have.lengthOf(2); - expect([...new Set(userIds)]).to.have.lengthOf(1); + expect(telemetryAnonymousIds).to.have.lengthOf(2); + expect([...new Set(telemetryAnonymousIds)]).to.have.lengthOf(1); }); it('emits error for invalid config', async() => { @@ -985,7 +985,7 @@ describe('CliRepl', () => { const connectEvents = requests.flatMap( req => JSON.parse(req.body).batch.filter(entry => entry.event === 'New Connection')); expect(connectEvents).to.have.lengthOf(1); - expect(connectEvents[0].userId).to.be.a('string'); + expect(connectEvents[0].anonymousId).to.be.a('string'); const { properties } = connectEvents[0]; expect(properties.mongosh_version).to.be.a('string'); expect(properties.session_id).to.be.a('string'); diff --git a/packages/cli-repl/src/cli-repl.ts b/packages/cli-repl/src/cli-repl.ts index 146d864043..ad8f337623 100644 --- a/packages/cli-repl/src/cli-repl.ts +++ b/packages/cli-repl/src/cli-repl.ts @@ -66,7 +66,7 @@ export type CliReplOptions = { } & Pick; /** The set of config options that is *always* available in config files stored on the file system. */ -type CliUserConfigOnDisk = Partial & Pick; +type CliUserConfigOnDisk = Partial & Pick; /** * The REPL used from the terminal. @@ -105,7 +105,7 @@ class CliRepl implements MongoshIOProvider { this.analyticsOptions = options.analyticsOptions; this.onExit = options.onExit; this.config = { - userId: new bson.ObjectId().toString(), + telemetryAnonymousId: new bson.ObjectId().toString(), enableTelemetry: true }; @@ -118,11 +118,11 @@ class CliRepl implements MongoshIOProvider { }) .on('new-config', (config: CliUserConfigOnDisk) => { this.setTelemetryEnabled(config.enableTelemetry); - this.bus.emit('mongosh:new-user', config.userId); + this.bus.emit('mongosh:new-user', config.telemetryAnonymousId); }) .on('update-config', (config: CliUserConfigOnDisk) => { this.setTelemetryEnabled(config.enableTelemetry); - this.bus.emit('mongosh:update-user', config.userId); + this.bus.emit('mongosh:update-user', { userId: config.userId, anonymousId: config.telemetryAnonymousId ?? config.userId }); }); this.mongocryptdManager = new MongocryptdManager( @@ -485,7 +485,7 @@ class CliRepl implements MongoshIOProvider { this.config[key] = value; if (key === 'enableTelemetry') { this.setTelemetryEnabled(this.config.enableTelemetry); - this.bus.emit('mongosh:update-user', this.config.userId); + this.bus.emit('mongosh:update-user', { userId: this.config.userId, anonymousId: this.config.telemetryAnonymousId }); } try { await this.configDirectory.writeConfigFile(this.config); @@ -499,7 +499,7 @@ class CliRepl implements MongoshIOProvider { * Implements listConfigOptions from the {@link ConfigProvider} interface. */ listConfigOptions(): string[] { - const hiddenKeys = ['userId', 'disableGreetingMessage', 'forceDisableTelemetry']; + const hiddenKeys = ['userId', 'telemetryAnonymousId', 'disableGreetingMessage', 'forceDisableTelemetry']; const keys = Object.keys(new CliUserConfig()); return keys.filter(key => !hiddenKeys.includes(key)); } diff --git a/packages/cli-repl/test/e2e.spec.ts b/packages/cli-repl/test/e2e.spec.ts index 6c22d8b7b9..04a147738d 100644 --- a/packages/cli-repl/test/e2e.spec.ts +++ b/packages/cli-repl/test/e2e.spec.ts @@ -884,7 +884,7 @@ describe('e2e', function() { describe('config file', () => { it('sets up a config file', async() => { const config = await readConfig(); - expect(config.userId).to.match(/^[a-f0-9]{24}$/); + expect(config.telemetryAnonymousId).to.match(/^[a-f0-9]{24}$/); expect(config.enableTelemetry).to.be.true; expect(config.disableGreetingMessage).to.be.true; }); diff --git a/packages/java-shell/package-lock.json b/packages/java-shell/package-lock.json index 183f21d6b7..62c8d93b92 100644 --- a/packages/java-shell/package-lock.json +++ b/packages/java-shell/package-lock.json @@ -708,9 +708,9 @@ "dev": true }, "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true }, "has-tostringtag": { diff --git a/packages/logging/src/analytics-helpers.spec.ts b/packages/logging/src/analytics-helpers.spec.ts index 864d3e9e17..f26ebb8fca 100644 --- a/packages/logging/src/analytics-helpers.spec.ts +++ b/packages/logging/src/analytics-helpers.spec.ts @@ -17,18 +17,18 @@ describe('ToggleableAnalytics', () => { const toggleable = new ToggleableAnalytics(target); expect(events).to.have.lengthOf(0); - toggleable.identify({ userId: 'me', traits: { platform: '1234' } }); - toggleable.track({ userId: 'me', event: 'something', properties: { mongosh_version: '1.2.3' } }); + toggleable.identify({ anonymousId: 'me', traits: { platform: '1234' } }); + toggleable.track({ anonymousId: 'me', event: 'something', properties: { mongosh_version: '1.2.3' } }); expect(events).to.have.lengthOf(0); toggleable.enable(); expect(events).to.have.lengthOf(2); - toggleable.track({ userId: 'me', event: 'something2', properties: { mongosh_version: '1.2.3' } }); + toggleable.track({ anonymousId: 'me', event: 'something2', properties: { mongosh_version: '1.2.3' } }); expect(events).to.have.lengthOf(3); toggleable.pause(); - toggleable.track({ userId: 'me', event: 'something3', properties: { mongosh_version: '1.2.3' } }); + toggleable.track({ anonymousId: 'me', event: 'something3', properties: { mongosh_version: '1.2.3' } }); expect(events).to.have.lengthOf(3); toggleable.disable(); @@ -36,9 +36,9 @@ describe('ToggleableAnalytics', () => { toggleable.enable(); expect(events).to.deep.equal([ - [ 'identify', { userId: 'me', traits: { platform: '1234' } } ], - [ 'track', { userId: 'me', event: 'something', properties: { mongosh_version: '1.2.3' } } ], - [ 'track', { userId: 'me', event: 'something2', properties: { mongosh_version: '1.2.3' } } ] + [ 'identify', { anonymousId: 'me', traits: { platform: '1234' } } ], + [ 'track', { anonymousId: 'me', event: 'something', properties: { mongosh_version: '1.2.3' } } ], + [ 'track', { anonymousId: 'me', event: 'something2', properties: { mongosh_version: '1.2.3' } } ] ]); }); }); diff --git a/packages/logging/src/analytics-helpers.ts b/packages/logging/src/analytics-helpers.ts index 2d573a095c..3d7b5ae1a3 100644 --- a/packages/logging/src/analytics-helpers.ts +++ b/packages/logging/src/analytics-helpers.ts @@ -3,12 +3,14 @@ */ export interface MongoshAnalytics { identify(message: { - userId: string, + userId?: string, + anonymousId: string, traits: { platform: string } }): void; track(message: { - userId: string, + userId?: string, + anonymousId: string, event: string, properties: { // eslint-disable-next-line camelcase diff --git a/packages/logging/src/setup-logger-and-telemetry.spec.ts b/packages/logging/src/setup-logger-and-telemetry.spec.ts index 501e8bb207..56c1caa129 100644 --- a/packages/logging/src/setup-logger-and-telemetry.spec.ts +++ b/packages/logging/src/setup-logger-and-telemetry.spec.ts @@ -10,7 +10,7 @@ describe('setupLoggerAndTelemetry', () => { let analyticsOutput: ['identify'|'track'|'log', any][]; let bus: MongoshBus; - const userId = '53defe995fa47e6c13102d9d'; + const telemetryAnonymousId = '53defe995fa47e6c13102d9d'; const logId = '5fb3c20ee1507e894e5340f3'; const logger = new MongoLogWriter(logId, `/tmp/${logId}_log`, { @@ -36,9 +36,8 @@ describe('setupLoggerAndTelemetry', () => { expect(logOutput).to.have.lengthOf(0); expect(analyticsOutput).to.be.empty; - bus.emit('mongosh:new-user', userId); - - bus.emit('mongosh:update-user', userId); + bus.emit('mongosh:new-user', telemetryAnonymousId); + bus.emit('mongosh:update-user', { anonymousId: telemetryAnonymousId }); bus.emit('mongosh:connect', { uri: 'mongodb://localhost/', is_localhost: true, @@ -121,7 +120,7 @@ describe('setupLoggerAndTelemetry', () => { expect(logOutput[i++].msg).to.equal('User updated'); expect(logOutput[i].msg).to.equal('Connecting to server'); expect(logOutput[i].attr.session_id).to.equal('5fb3c20ee1507e894e5340f3'); - expect(logOutput[i].attr.userId).to.equal('53defe995fa47e6c13102d9d'); + expect(logOutput[i].attr.telemetryAnonymousId).to.equal('53defe995fa47e6c13102d9d'); expect(logOutput[i].attr.connectionUri).to.equal('mongodb://localhost/'); expect(logOutput[i].attr.is_localhost).to.equal(true); expect(logOutput[i].attr.is_atlas).to.equal(false); @@ -215,7 +214,7 @@ describe('setupLoggerAndTelemetry', () => { [ 'identify', { - userId: '53defe995fa47e6c13102d9d', + anonymousId: '53defe995fa47e6c13102d9d', traits: { platform: process.platform, arch: process.arch @@ -225,7 +224,7 @@ describe('setupLoggerAndTelemetry', () => { [ 'identify', { - userId: '53defe995fa47e6c13102d9d', + anonymousId: '53defe995fa47e6c13102d9d', traits: { platform: process.platform, arch: process.arch @@ -235,7 +234,7 @@ describe('setupLoggerAndTelemetry', () => { [ 'track', { - userId: '53defe995fa47e6c13102d9d', + anonymousId: '53defe995fa47e6c13102d9d', event: 'New Connection', properties: { mongosh_version: '1.0.0', @@ -249,7 +248,7 @@ describe('setupLoggerAndTelemetry', () => { [ 'track', { - userId: '53defe995fa47e6c13102d9d', + anonymousId: '53defe995fa47e6c13102d9d', event: 'Error', properties: { mongosh_version: '1.0.0', @@ -263,7 +262,7 @@ describe('setupLoggerAndTelemetry', () => { [ 'track', { - userId: '53defe995fa47e6c13102d9d', + anonymousId: '53defe995fa47e6c13102d9d', event: 'Error', properties: { mongosh_version: '1.0.0', @@ -277,7 +276,7 @@ describe('setupLoggerAndTelemetry', () => { [ 'track', { - userId: '53defe995fa47e6c13102d9d', + anonymousId: '53defe995fa47e6c13102d9d', event: 'Use', properties: { mongosh_version: '1.0.0' } } @@ -285,7 +284,7 @@ describe('setupLoggerAndTelemetry', () => { [ 'track', { - userId: '53defe995fa47e6c13102d9d', + anonymousId: '53defe995fa47e6c13102d9d', event: 'Show', properties: { mongosh_version: '1.0.0', @@ -302,7 +301,7 @@ describe('setupLoggerAndTelemetry', () => { nested: true, shell: true }, - userId: '53defe995fa47e6c13102d9d' + anonymousId: '53defe995fa47e6c13102d9d' } ], [ @@ -313,7 +312,7 @@ describe('setupLoggerAndTelemetry', () => { mongosh_version: '1.0.0', nested: false }, - userId: '53defe995fa47e6c13102d9d' + anonymousId: '53defe995fa47e6c13102d9d' } ], [ @@ -323,7 +322,7 @@ describe('setupLoggerAndTelemetry', () => { properties: { mongosh_version: '1.0.0', }, - userId: '53defe995fa47e6c13102d9d' + anonymousId: '53defe995fa47e6c13102d9d' } ], [ @@ -333,7 +332,7 @@ describe('setupLoggerAndTelemetry', () => { properties: { mongosh_version: '1.0.0', }, - userId: '53defe995fa47e6c13102d9d' + anonymousId: '53defe995fa47e6c13102d9d' } ], [ @@ -344,13 +343,13 @@ describe('setupLoggerAndTelemetry', () => { mongosh_version: '1.0.0', shell: true }, - userId: '53defe995fa47e6c13102d9d' + anonymousId: '53defe995fa47e6c13102d9d' } ], [ 'track', { - userId: '53defe995fa47e6c13102d9d', + anonymousId: '53defe995fa47e6c13102d9d', event: 'Snippet Install', properties: { mongosh_version: '1.0.0' @@ -365,7 +364,7 @@ describe('setupLoggerAndTelemetry', () => { expect(logOutput).to.have.lengthOf(0); expect(analyticsOutput).to.be.empty; - bus.emit('mongosh:new-user', userId); + bus.emit('mongosh:new-user', telemetryAnonymousId); logOutput = []; analyticsOutput = []; @@ -394,7 +393,7 @@ describe('setupLoggerAndTelemetry', () => { [ 'track', { - userId: '53defe995fa47e6c13102d9d', + anonymousId: '53defe995fa47e6c13102d9d', event: 'Deprecated Method', properties: { mongosh_version: '1.0.0', @@ -406,7 +405,7 @@ describe('setupLoggerAndTelemetry', () => { [ 'track', { - userId: '53defe995fa47e6c13102d9d', + anonymousId: '53defe995fa47e6c13102d9d', event: 'Deprecated Method', properties: { mongosh_version: '1.0.0', @@ -418,7 +417,7 @@ describe('setupLoggerAndTelemetry', () => { [ 'track', { - userId: '53defe995fa47e6c13102d9d', + anonymousId: '53defe995fa47e6c13102d9d', event: 'Deprecated Method', properties: { mongosh_version: '1.0.0', @@ -430,7 +429,7 @@ describe('setupLoggerAndTelemetry', () => { [ 'track', { - userId: '53defe995fa47e6c13102d9d', + anonymousId: '53defe995fa47e6c13102d9d', event: 'API Call', properties: { mongosh_version: '1.0.0', @@ -443,7 +442,7 @@ describe('setupLoggerAndTelemetry', () => { [ 'track', { - userId: '53defe995fa47e6c13102d9d', + anonymousId: '53defe995fa47e6c13102d9d', event: 'API Call', properties: { mongosh_version: '1.0.0', @@ -455,7 +454,7 @@ describe('setupLoggerAndTelemetry', () => { ], ]); - bus.emit('mongosh:new-user', userId); + bus.emit('mongosh:new-user', telemetryAnonymousId); logOutput = []; analyticsOutput = []; diff --git a/packages/logging/src/setup-logger-and-telemetry.ts b/packages/logging/src/setup-logger-and-telemetry.ts index 00280e487b..afb9586855 100644 --- a/packages/logging/src/setup-logger-and-telemetry.ts +++ b/packages/logging/src/setup-logger-and-telemetry.ts @@ -29,7 +29,8 @@ import type { SnippetsTransformErrorEvent, EditorRunEditCommandEvent, EditorReadVscodeExtensionsDoneEvent, - EditorReadVscodeExtensionsFailedEvent + EditorReadVscodeExtensionsFailedEvent, + TelemetryUserIdentity } from '@mongosh/types'; import { inspect } from 'util'; import { MongoLogWriter, mongoLogId } from 'mongodb-log-writer'; @@ -72,7 +73,7 @@ export function setupLoggerAndTelemetry( userTraits: any, mongosh_version: string): void { const { logId } = log; - let userId: string; + let telemetryUserIdentity: TelemetryUserIdentity; // We emit different analytics events for loading files and evaluating scripts // depending on whether we're already in the REPL or not yet. We store the @@ -93,11 +94,17 @@ export function setupLoggerAndTelemetry( bus.on('mongosh:connect', function(args: ConnectEvent) { const connectionUri = redactURICredentials(args.uri); const { uri: _uri, ...argsWithoutUri } = args; // eslint-disable-line @typescript-eslint/no-unused-vars - const params = { session_id: logId, userId, connectionUri, ...argsWithoutUri }; + const params = { + session_id: logId, + userId: telemetryUserIdentity?.userId, + telemetryAnonymousId: telemetryUserIdentity?.anonymousId, + connectionUri, + ...argsWithoutUri + }; log.info('MONGOSH', mongoLogId(1_000_000_004), 'connect', 'Connecting to server', params); analytics.track({ - userId, + ...telemetryUserIdentity, event: 'New Connection', properties: { mongosh_version, @@ -107,14 +114,24 @@ export function setupLoggerAndTelemetry( }); }); - bus.on('mongosh:new-user', function(id: string) { - userId = id; - analytics.identify({ userId, traits: userTraits }); + bus.on('mongosh:new-user', function(anonymousId: string) { + telemetryUserIdentity = { anonymousId }; + analytics.identify({ anonymousId, traits: userTraits }); }); - bus.on('mongosh:update-user', function(id: string) { - userId = id; - analytics.identify({ userId, traits: userTraits }); + bus.on('mongosh:update-user', function(updatedTelemetryUserIdentity: TelemetryUserIdentity) { + telemetryUserIdentity = updatedTelemetryUserIdentity; + + const telemetryIdentifyArg: TelemetryUserIdentity & { traits: any } = { + anonymousId: telemetryUserIdentity.anonymousId, + traits: userTraits + }; + + if (telemetryUserIdentity?.userId) { + telemetryIdentifyArg.userId = telemetryUserIdentity.userId; + } + + analytics.identify(telemetryIdentifyArg); log.info('MONGOSH', mongoLogId(1_000_000_005), 'config', 'User updated'); }); @@ -127,7 +144,7 @@ export function setupLoggerAndTelemetry( if (error.name.includes('Mongosh')) { analytics.track({ - userId, + ...telemetryUserIdentity, event: 'Error', properties: { mongosh_version, @@ -152,7 +169,7 @@ export function setupLoggerAndTelemetry( log.info('MONGOSH', mongoLogId(1_000_000_008), 'shell-api', 'Used "use" command', args); analytics.track({ - userId, + ...telemetryUserIdentity, event: 'Use', properties: { mongosh_version @@ -164,7 +181,7 @@ export function setupLoggerAndTelemetry( log.info('MONGOSH', mongoLogId(1_000_000_009), 'shell-api', 'Used "show" command', args); analytics.track({ - userId, + ...telemetryUserIdentity, event: 'Show', properties: { mongosh_version, @@ -192,7 +209,7 @@ export function setupLoggerAndTelemetry( log.info('MONGOSH', mongoLogId(1_000_000_012), 'shell-api', 'Loading file via load()', args); analytics.track({ - userId, + ...telemetryUserIdentity, event: hasStartedMongoshRepl ? 'Script Loaded' : 'Script Loaded CLI', properties: { mongosh_version, @@ -206,7 +223,7 @@ export function setupLoggerAndTelemetry( log.info('MONGOSH', mongoLogId(1_000_000_013), 'repl', 'Evaluating script passed on the command line'); analytics.track({ - userId, + ...telemetryUserIdentity, event: 'Script Evaluated', properties: { mongosh_version, @@ -219,7 +236,7 @@ export function setupLoggerAndTelemetry( log.info('MONGOSH', mongoLogId(1_000_000_014), 'repl', 'Loading .mongoshrc.js'); analytics.track({ - userId, + ...telemetryUserIdentity, event: 'Mongoshrc Loaded', properties: { mongosh_version @@ -231,7 +248,7 @@ export function setupLoggerAndTelemetry( log.info('MONGOSH', mongoLogId(1_000_000_015), 'repl', 'Warning about .mongorc.js/.mongoshrc.js mismatch'); analytics.track({ - userId, + ...telemetryUserIdentity, event: 'Mongorc Warning', properties: { mongosh_version @@ -307,7 +324,7 @@ export function setupLoggerAndTelemetry( if (ev.args[0] === 'install') { analytics.track({ - userId, + ...telemetryUserIdentity, event: 'Snippet Install', properties: { mongosh_version @@ -343,7 +360,7 @@ export function setupLoggerAndTelemetry( log.warn('MONGOSH', mongoLogId(1_000_000_033), 'shell-api', 'Deprecated API call', entry); analytics.track({ - userId, + ...telemetryUserIdentity, event: 'Deprecated Method', properties: { mongosh_version, @@ -353,7 +370,7 @@ export function setupLoggerAndTelemetry( } for (const [entry, count] of apiCalls) { analytics.track({ - userId, + ...telemetryUserIdentity, event: 'API Call', properties: { mongosh_version, diff --git a/packages/types/src/index.spec.ts b/packages/types/src/index.spec.ts index cb5ee883c6..475bf38d87 100644 --- a/packages/types/src/index.spec.ts +++ b/packages/types/src/index.spec.ts @@ -5,6 +5,7 @@ describe('config validation', () => { it('validates config option values', async() => { const { validate } = CliUserConfigValidator as any; expect(await validate('userId', 'foo')).to.equal(null); + expect(await validate('telemetryAnonymousId', 'foo')).to.equal(null); expect(await validate('disableGreetingMessage', 'foo')).to.equal(null); expect(await validate('inspectDepth', 'foo')).to.equal('inspectDepth must be a positive integer'); expect(await validate('inspectDepth', -1)).to.equal('inspectDepth must be a positive integer'); diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 142df7a528..0eef4f9b5e 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -167,6 +167,11 @@ export interface EditorReadVscodeExtensionsFailedEvent { error: Error; } +export interface TelemetryUserIdentity { + userId?: string; + anonymousId: string; +} + export interface MongoshBusEventsMap extends ConnectEventMap { /** * Signals a connection to a MongoDB instance has been established @@ -180,7 +185,7 @@ export interface MongoshBusEventsMap extends ConnectEventMap { /** * Signals a change of the user telemetry settings. */ - 'mongosh:update-user': (id: string) => void; + 'mongosh:update-user': (identity: TelemetryUserIdentity) => void; /** * Signals an error that should be logged or potentially tracked by analytics. */ @@ -412,7 +417,8 @@ export class SnippetShellUserConfigValidator extends ShellUserConfigValidator { } export class CliUserConfig extends SnippetShellUserConfig { - userId = ''; + userId?: string; + telemetryAnonymousId = ''; disableGreetingMessage = false; forceDisableTelemetry = false; inspectCompact: number | boolean = 3; @@ -427,6 +433,7 @@ export class CliUserConfigValidator extends SnippetShellUserConfigValidator { static async validate(key: K, value: CliUserConfig[K]): Promise { switch (key) { case 'userId': + case 'telemetryAnonymousId': case 'disableGreetingMessage': return null; // Not modifiable by the user anyway. case 'inspectCompact':