Skip to content

Commit

Permalink
chore(cli-repl): add telemetry for loading JS files MONGOSH-627 (#733)
Browse files Browse the repository at this point in the history
As defined in the ticket.
  • Loading branch information
addaleax committed Mar 22, 2021
1 parent 3f7f144 commit f393348
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 5 deletions.
12 changes: 12 additions & 0 deletions packages/cli-repl/src/cli-repl.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,18 @@ describe('CliRepl', () => {
req => JSON.parse(req.body).batch.filter(entry => entry.event === 'Use')).flat();
expect(useEvents).to.have.lengthOf(2);
});

it('posts analytics event for load() calls', async() => {
const filenameB = path.resolve(__dirname, '..', 'test', 'fixtures', 'load', 'b.js');
input.write(`load(${JSON.stringify(filenameB)});\n`);
input.write('exit\n');
await waitBus(cliRepl.bus, 'mongosh:closed');
const loadEvents = requests.map(
req => JSON.parse(req.body).batch.filter(entry => entry.event === 'Script Loaded')).flat();
expect(loadEvents).to.have.lengthOf(2);
expect(loadEvents[0].properties.nested).to.equal(false);
expect(loadEvents[1].properties.nested).to.equal(true);
});
});

context('without network connectivity', () => {
Expand Down
5 changes: 5 additions & 0 deletions packages/cli-repl/src/cli-repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,15 @@ class CliRepl {
const initialized = await this.mongoshRepl.initialize(initialServiceProvider);
const commandLineLoadFiles = this.listCommandLineLoadFiles();
if (commandLineLoadFiles.length > 0 || this.cliOptions.eval !== undefined) {
this.bus.emit('mongosh:start-loading-cli-scripts', { usesShellOption: !!this.cliOptions.shell });
await this.loadCommandLineFilesAndEval(commandLineLoadFiles);
if (!this.cliOptions.shell) {
await this.exit(0);
return;
}
}
await this.loadRcFiles();
this.bus.emit('mongosh:start-mongosh-repl');
await this.mongoshRepl.startRepl(initialized);
}

Expand All @@ -170,6 +172,7 @@ class CliRepl {

async loadCommandLineFilesAndEval(files: string[]) {
if (this.cliOptions.eval) {
this.bus.emit('mongosh:eval-cli-script');
await this.mongoshRepl.loadExternalCode(this.cliOptions.eval, '@(shell eval)');
} else if (this.cliOptions.eval === '') {
// This happens e.g. when --eval is followed by another option, for example
Expand Down Expand Up @@ -201,6 +204,7 @@ class CliRepl {
} catch { /* file not present */ }
if (hasMongoshRc) {
try {
this.bus.emit('mongosh:mongoshrc-load');
await this.mongoshRepl.loadExternalFile(mongoshrcPath);
} catch (err) {
this.output.write(this.mongoshRepl.writer(err) + '\n');
Expand All @@ -218,6 +222,7 @@ class CliRepl {
hasLegacyRc = true;
} catch { /* file not present */ }
if (hasLegacyRc) {
this.bus.emit('mongosh:mongoshrc-mongorc-warn');
const msg =
'Warning: Found ~/.mongorc.js, but not ~/.mongoshrc.js. ~/.mongorc.js will not be loaded.\n' +
' You may want to copy or rename ~/.mongorc.js to ~/.mongoshrc.js.\n';
Expand Down
75 changes: 74 additions & 1 deletion packages/cli-repl/src/setup-logger-and-telemetry.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,15 @@ describe('setupLoggerAndTelemetry', () => {
bus.emit('mongosh:rewritten-async-input', { original: '1+1', rewritten: '2' });
bus.emit('mongosh:driver-initialized', { driver: { name: 'nodejs', version: '3.6.1' } });

expect(logOutput).to.have.lengthOf(17);
bus.emit('mongosh:start-loading-cli-scripts', { usesShellOption: true });
bus.emit('mongosh:api-load-file', { nested: true, filename: 'foobar.js' });
bus.emit('mongosh:start-mongosh-repl');
bus.emit('mongosh:api-load-file', { nested: false, filename: 'foobar.js' });
bus.emit('mongosh:mongoshrc-load');
bus.emit('mongosh:mongoshrc-mongorc-warn');
bus.emit('mongosh:eval-cli-script');

expect(logOutput).to.have.lengthOf(24);
expect(logOutput[0].msg).to.equal('mongosh:update-user {"enableTelemetry":false}');
expect(logOutput[1].msg).to.match(/^mongosh:connect/);
expect(logOutput[1].msg).to.match(/"session_id":"5fb3c20ee1507e894e5340f3"/);
Expand Down Expand Up @@ -86,6 +94,17 @@ describe('setupLoggerAndTelemetry', () => {
expect(logOutput[15].msg).to.match(/"original":"1\+1"/);
expect(logOutput[15].msg).to.match(/"rewritten":"2"/);
expect(logOutput[16].msg).to.match(/"version":"3.6.1"/);
expect(logOutput[17].msg).to.equal('mongosh:start-loading-cli-scripts');
expect(logOutput[18].msg).to.match(/^mongosh:api-load-file/);
expect(logOutput[18].msg).to.match(/"nested":true/);
expect(logOutput[18].msg).to.match(/"filename":"foobar.js"/);
expect(logOutput[19].msg).to.equal('mongosh:start-mongosh-repl');
expect(logOutput[20].msg).to.match(/"nested":false/);
expect(logOutput[20].msg).to.match(/"filename":"foobar.js"/);
expect(logOutput[21].msg).to.equal('mongosh:mongoshrc-load');
expect(logOutput[22].msg).to.equal('mongosh:mongoshrc-mongorc-warn');
expect(logOutput[23].msg).to.equal('mongosh:eval-cli-script');


const mongosh_version = require('../package.json').version;
expect(analyticsOutput).to.deep.equal([
Expand Down Expand Up @@ -145,6 +164,60 @@ describe('setupLoggerAndTelemetry', () => {
method: 'dbs'
}
}
],
[
'track',
{
event: 'Script Loaded CLI',
properties: {
mongosh_version,
nested: true,
shell: true
},
userId: '53defe995fa47e6c13102d9d'
}
],
[
'track',
{
event: 'Script Loaded',
properties: {
mongosh_version,
nested: false
},
userId: '53defe995fa47e6c13102d9d'
}
],
[
'track',
{
event: 'Mongoshrc Loaded',
properties: {
mongosh_version,
},
userId: '53defe995fa47e6c13102d9d'
}
],
[
'track',
{
event: 'Mongorc Warning',
properties: {
mongosh_version,
},
userId: '53defe995fa47e6c13102d9d'
}
],
[
'track',
{
event: 'Script Evaluated',
properties: {
mongosh_version,
shell: true
},
userId: '53defe995fa47e6c13102d9d'
}
]
]);
});
Expand Down
79 changes: 78 additions & 1 deletion packages/cli-repl/src/setup-logger-and-telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import type {
UseEvent,
AsyncRewriterEvent,
ShowEvent,
ConnectEvent
ConnectEvent,
ScriptLoadFileEvent,
StartLoadingCliScriptsEvent
} from '@mongosh/types';

interface MongoshAnalytics {
Expand Down Expand Up @@ -50,6 +52,22 @@ export default function setupLoggerAndTelemetry(
log.error(e);
}

// 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
// state here so that the places where the events are emitted don't have to
// be aware of this distinction.
let hasStartedMongoshRepl = false;
bus.on('mongosh:start-mongosh-repl', () => {
log.info('mongosh:start-mongosh-repl');
hasStartedMongoshRepl = true;
});

let usesShellOption = false;
bus.on('mongosh:start-loading-cli-scripts', (event: StartLoadingCliScriptsEvent) => {
log.info('mongosh:start-loading-cli-scripts');
usesShellOption = event.usesShellOption;
});

bus.on('mongosh:connect', function(args: ConnectEvent) {
const connectionUri = redactPassword(args.uri);
const { uri: _uri, ...argsWithoutUri } = args; // eslint-disable-line @typescript-eslint/no-unused-vars
Expand Down Expand Up @@ -158,4 +176,63 @@ export default function setupLoggerAndTelemetry(
bus.on('mongosh:api-call', function(args: ApiEvent) {
log.info('mongosh:api-call', redactInfo(args));
});

bus.on('mongosh:api-load-file', function(args: ScriptLoadFileEvent) {
log.info('mongosh:api-load-file', args);

if (telemetry) {
analytics.track({
userId,
event: hasStartedMongoshRepl ? 'Script Loaded' : 'Script Loaded CLI',
properties: {
mongosh_version,
nested: args.nested,
...(hasStartedMongoshRepl ? {} : { shell: usesShellOption })
}
});
}
});

bus.on('mongosh:eval-cli-script', function() {
log.info('mongosh:eval-cli-script');

if (telemetry) {
analytics.track({
userId,
event: 'Script Evaluated',
properties: {
mongosh_version,
shell: usesShellOption
}
});
}
});

bus.on('mongosh:mongoshrc-load', function() {
log.info('mongosh:mongoshrc-load');

if (telemetry) {
analytics.track({
userId,
event: 'Mongoshrc Loaded',
properties: {
mongosh_version
}
});
}
});

bus.on('mongosh:mongoshrc-mongorc-warn', function() {
log.info('mongosh:mongoshrc-mongorc-warn');

if (telemetry) {
analytics.track({
userId,
event: 'Mongorc Warning',
properties: {
mongosh_version
}
});
}
});
}
28 changes: 25 additions & 3 deletions packages/shell-api/src/shell-api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import ShellApi from './shell-api';
import { signatures, toShellResult } from './index';
import Cursor from './cursor';
import { ALL_PLATFORMS, ALL_SERVER_VERSIONS, ALL_TOPOLOGIES } from './enums';
import { StubbedInstance, stubInterface } from 'ts-sinon';
import sinon, { StubbedInstance, stubInterface } from 'ts-sinon';
import Mongo from './mongo';
import { ReplPlatform, ServiceProvider, bson, MongoClient } from '@mongosh/service-provider-core';
import { EventEmitter } from 'events';
Expand Down Expand Up @@ -135,12 +135,12 @@ describe('ShellApi', () => {
let serviceProvider: StubbedInstance<ServiceProvider>;
let newSP: StubbedInstance<ServiceProvider>;
let rawClientStub: StubbedInstance<MongoClient>;
let bus: StubbedInstance<EventEmitter>;
let bus: EventEmitter;
let internalState: ShellInternalState;
let mongo: Mongo;

beforeEach(() => {
bus = stubInterface<EventEmitter>();
bus = new EventEmitter();
rawClientStub = stubInterface<MongoClient>();
newSP = stubInterface<ServiceProvider>();
newSP.initialDb = 'test';
Expand Down Expand Up @@ -570,6 +570,8 @@ describe('ShellApi', () => {
});
describe('load', () => {
it('asks the evaluation listener to load a file', async() => {
const apiLoadFileListener = sinon.stub();
bus.on('mongosh:api-load-file', apiLoadFileListener);
evaluationListener.onLoad.callsFake(async(filename: string) => {
expect(filename).to.equal('abc.js');
expect(internalState.context.__filename).to.equal(undefined);
Expand All @@ -586,6 +588,26 @@ describe('ShellApi', () => {
expect(evaluationListener.onLoad).to.have.callCount(1);
expect(internalState.context.__filename).to.equal(undefined);
expect(internalState.context.__dirname).to.equal(undefined);
expect(apiLoadFileListener).to.have.been.calledWith({ nested: false, filename: 'abc.js' });
});
it('emits different events depending on nesting level', async() => {
const apiLoadFileListener = sinon.stub();
bus.on('mongosh:api-load-file', apiLoadFileListener);
evaluationListener.onLoad.callsFake(async(filename: string) => {
return {
resolvedFilename: '/resolved/' + filename,
evaluate: async() => {
if (filename === 'def.js') {
return;
}
await internalState.context.load('def.js');
}
};
});
await internalState.context.load('abc.js');
expect(apiLoadFileListener).to.have.callCount(2);
expect(apiLoadFileListener).to.have.been.calledWith({ nested: false, filename: 'abc.js' });
expect(apiLoadFileListener).to.have.been.calledWith({ nested: true, filename: 'def.js' });
});
});
for (const cmd of ['print', 'printjson']) {
Expand Down
8 changes: 8 additions & 0 deletions packages/shell-api/src/shell-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ import { dirname } from 'path';
export default class ShellApi extends ShellApiClass {
readonly internalState: ShellInternalState;
public DBQuery: DBQuery;
loadCallNestingLevel: number;

constructor(internalState: ShellInternalState) {
super();
this.internalState = internalState;
this.DBQuery = new DBQuery();
this.loadCallNestingLevel = 0;
}

@directShellCommand
Expand Down Expand Up @@ -110,6 +112,10 @@ export default class ShellApi extends ShellApiClass {
CommonErrors.NotImplemented
);
}
this.internalState.messageBus.emit('mongosh:api-load-file', {
nested: this.loadCallNestingLevel > 0,
filename
});
const {
resolvedFilename, evaluate
} = await this.internalState.evaluationListener.onLoad(filename);
Expand All @@ -118,9 +124,11 @@ export default class ShellApi extends ShellApiClass {
const previousFilename = context.__filename;
context.__filename = resolvedFilename;
context.__dirname = dirname(resolvedFilename);
this.loadCallNestingLevel++;
try {
await evaluate();
} finally {
this.loadCallNestingLevel--;
if (previousFilename) {
context.__filename = previousFilename;
context.__dirname = dirname(previousFilename);
Expand Down
15 changes: 15 additions & 0 deletions packages/types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ export interface ConnectEvent {
uri: string;
}

export interface ScriptLoadFileEvent {
nested: boolean;
filename: string;
}

export interface StartLoadingCliScriptsEvent {
usesShellOption: boolean;
}

export interface MongoshBusEventsMap {
'mongosh:connect': (ev: ConnectEvent) => void;
'mongosh:driver-initialized': (driverMetadata: any) => void;
Expand All @@ -62,6 +71,12 @@ export interface MongoshBusEventsMap {
'mongosh:setCtx': (ev: ApiEvent) => void;
'mongosh:api-call': (ev: ApiEvent) => void;
'mongosh:warn': (ev: ApiWarning) => void;
'mongosh:api-load-file': (ev: ScriptLoadFileEvent) => void;
'mongosh:start-loading-cli-scripts': (event: StartLoadingCliScriptsEvent) => void;
'mongosh:start-mongosh-repl': () => void;
'mongosh:mongoshrc-load': () => void;
'mongosh:mongoshrc-mongorc-warn': () => void;
'mongosh:eval-cli-script': () => void;
'mongosh:closed': () => void; // For testing.
'mongosh:eval-complete': () => void; // For testing.
'mongosh:autocompletion-complete': () => void; // For testing.
Expand Down

0 comments on commit f393348

Please sign in to comment.