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

Added App Insights instrumentation. #1251

Merged
merged 1 commit into from Jan 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Expand Up @@ -7,4 +7,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Added
- [docs] Added changelog in PR [#1230](https://github.com/Microsoft/BotFramework-Emulator/pull/1230)
- [style] 💅 integrate prettier and eslint ([#1240](https://github.com/Microsoft/BotFramework-Emulator/pull/1240))
- [style] 💅 Integrated prettier and eslint in PR [#1240](https://github.com/Microsoft/BotFramework-Emulator/pull/1240)
- [feat] Added app-wide instrumentation in PR [#1251](https://github.com/Microsoft/BotFramework-Emulator/pull/1251)
28 changes: 28 additions & 0 deletions package-lock.json

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

13 changes: 13 additions & 0 deletions packages/app/client/src/commands/botCommands.spec.ts
Expand Up @@ -87,12 +87,25 @@ describe('The bot commands', () => {
});

it('should make the appropriate calls to switch bots', () => {
const remoteCallArgs = [];
CommandServiceImpl.remoteCall = async (...args: any[]) => {
remoteCallArgs.push(args);
return true;
};
const spy = jest.spyOn(ActiveBotHelper, 'confirmAndSwitchBots');
const { handler } = registry.getCommand(
SharedConstants.Commands.Bot.Switch
);
handler({});
expect(spy).toHaveBeenCalledWith({});
expect(remoteCallArgs[0][0]).toBe(
SharedConstants.Commands.Telemetry.TrackEvent
);
expect(remoteCallArgs[0][1]).toBe('bot_open');
expect(remoteCallArgs[0][2]).toEqual({
method: 'bots_list',
numOfServices: undefined,
});
});

it('should make the appropriate calls to close a bot', () => {
Expand Down
13 changes: 11 additions & 2 deletions packages/app/client/src/commands/botCommands.ts
Expand Up @@ -60,8 +60,17 @@ export function registerCommands(commandRegistry: CommandRegistryImpl) {
// Switches the current active bot
commandRegistry.registerCommand(
Commands.Bot.Switch,
(bot: BotConfigWithPath | string) =>
ActiveBotHelper.confirmAndSwitchBots(bot)
(bot: BotConfigWithPath | string) => {
let numOfServices;
if (typeof bot !== 'string') {
numOfServices = bot.services && bot.services.length;
}
CommandServiceImpl.remoteCall(Commands.Telemetry.TrackEvent, 'bot_open', {
method: 'bots_list',
numOfServices,
}).catch(_e => void 0);
return ActiveBotHelper.confirmAndSwitchBots(bot);
}
);

// ---------------------------------------------------------------------------
Expand Down
9 changes: 8 additions & 1 deletion packages/app/client/src/commands/emulatorCommands.spec.ts
Expand Up @@ -127,7 +127,9 @@ describe('The emulator commands', () => {
const remoteCallSpy = jest
.spyOn(CommandServiceImpl, 'remoteCall')
.mockResolvedValue('transcript.transcript');
const callSpy = jest.spyOn(CommandServiceImpl, 'call');
const callSpy = jest
.spyOn(CommandServiceImpl, 'call')
.mockResolvedValue(null);

await handler();

Expand All @@ -140,6 +142,11 @@ describe('The emulator commands', () => {
title: 'Open transcript file',
}
);
expect(remoteCallSpy).toHaveBeenCalledWith(
SharedConstants.Commands.Telemetry.TrackEvent,
'transcriptFile_open',
{ method: 'file_menu' }
);

expect(callSpy).toHaveBeenCalledWith(
'transcript:open',
Expand Down
15 changes: 14 additions & 1 deletion packages/app/client/src/commands/emulatorCommands.ts
Expand Up @@ -35,6 +35,7 @@ import { newNotification, SharedConstants } from '@bfemulator/app-shared';
import {
Activity,
CommandRegistryImpl,
isLocalHostUrl,
uniqueId,
} from '@bfemulator/sdk-shared';
import { IEndpointService } from 'botframework-config/lib/schema';
Expand All @@ -49,7 +50,10 @@ import { CommandServiceImpl } from '../platform/commands/commandServiceImpl';

/** Registers emulator (actual conversation emulation logic) commands */
export function registerCommands(commandRegistry: CommandRegistryImpl) {
const { Emulator } = SharedConstants.Commands;
const {
Emulator,
Telemetry: { TrackEvent },
} = SharedConstants.Commands;

// ---------------------------------------------------------------------------
// Open a new emulator tabbed document
Expand Down Expand Up @@ -79,6 +83,12 @@ export function registerCommands(commandRegistry: CommandRegistryImpl) {
);
}

if (!isLocalHostUrl(endpoint.endpoint)) {
CommandServiceImpl.remoteCall(TrackEvent, 'livechat_openRemote').catch(
_e => void 0
);
}

store.dispatch(
EditorActions.open({
contentType: Constants.CONTENT_TYPE_LIVE_CHAT,
Expand Down Expand Up @@ -140,6 +150,9 @@ export function registerCommands(commandRegistry: CommandRegistryImpl) {
dialogOptions
);
await CommandServiceImpl.call(Emulator.OpenTranscript, filename);
CommandServiceImpl.remoteCall(TrackEvent, 'transcriptFile_open', {
method: 'file_menu',
}).catch(_e => void 0);
} catch (e) {
const errMsg = `Error while opening transcript file: ${e}`;
const notification = newNotification(errMsg);
Expand Down
8 changes: 8 additions & 0 deletions packages/app/client/src/commands/uiCommands.spec.ts
Expand Up @@ -57,8 +57,10 @@ import {
OpenBotDialogContainer,
SecretPromptDialogContainer,
} from '../ui/dialogs';
import { CommandServiceImpl } from '../platform/commands/commandServiceImpl';

import { registerCommands } from './uiCommands';

jest.mock('../ui/dialogs', () => ({
AzureLoginPromptDialogContainer: class {},
AzureLoginSuccessDialogContainer: class {},
Expand Down Expand Up @@ -153,10 +155,16 @@ describe('the uiCommands', () => {
});

it('should set the proper href on the theme tag when the SwitchTheme command is dispatched', () => {
const remoteCallSpy = jest.spyOn(CommandServiceImpl, 'remoteCall');
const link = document.createElement('link');
link.id = 'themeVars';
document.querySelector('head').appendChild(link);
registry.getCommand(Commands.SwitchTheme).handler('light', './light.css');
expect(link.href).toBe('http://localhost/light.css');
expect(remoteCallSpy).toHaveBeenCalledWith(
SharedConstants.Commands.Telemetry.TrackEvent,
'app_chooseTheme',
{ themeName: 'light' }
);
});
});
6 changes: 5 additions & 1 deletion packages/app/client/src/commands/uiCommands.ts
Expand Up @@ -51,6 +51,7 @@ import { switchTheme } from '../data/action/themeActions';
import { showWelcomePage } from '../data/editorHelpers';
import { AzureAuthState } from '../data/reducer/azureAuthReducer';
import { store } from '../data/store';
import { CommandServiceImpl } from '../platform/commands/commandServiceImpl';
import {
AzureLoginFailedDialogContainer,
AzureLoginPromptDialogContainer,
Expand All @@ -67,7 +68,7 @@ import {

/** Register UI commands (toggling UI) */
export function registerCommands(commandRegistry: CommandRegistry) {
const { UI } = SharedConstants.Commands;
const { UI, Telemetry } = SharedConstants.Commands;

// ---------------------------------------------------------------------------
// Shows the welcome page
Expand Down Expand Up @@ -136,6 +137,9 @@ export function registerCommands(commandRegistry: CommandRegistry) {
link => link.href
); // href is fully qualified
store.dispatch(switchTheme(themeName, themeComponents));
CommandServiceImpl.remoteCall(Telemetry.TrackEvent, 'app_chooseTheme', {
themeName,
}).catch(_e => void 0);
}
);

Expand Down
8 changes: 8 additions & 0 deletions packages/app/client/src/data/sagas/azureAuthSaga.spec.ts
Expand Up @@ -182,6 +182,10 @@ describe('The azureAuthSaga', () => {
ct++;
}
expect(ct).toBe(5);
expect(remoteCallSpy).toHaveBeenCalledWith(
SharedConstants.Commands.Telemetry.TrackEvent,
'signIn_failure'
);
});

it('should contain 6 steps when the Azure login dialog prompt is confirmed and auth succeeds', async () => {
Expand Down Expand Up @@ -257,6 +261,10 @@ describe('The azureAuthSaga', () => {
expect(store.getState().azureAuth.access_token).toBe(
'a valid access_token'
);
expect(remoteCallSpy).toHaveBeenCalledWith(
SharedConstants.Commands.Telemetry.TrackEvent,
'signIn_success'
);
});
});
});
7 changes: 7 additions & 0 deletions packages/app/client/src/data/sagas/azureAuthSaga.ts
Expand Up @@ -66,6 +66,7 @@ export function* getArmToken(
RetrieveArmToken,
PersistAzureLoginChanged,
} = SharedConstants.Commands.Azure;
const { TrackEvent } = SharedConstants.Commands.Telemetry;
azureAuth = yield call(
CommandServiceImpl.remoteCall.bind(CommandServiceImpl),
RetrieveArmToken
Expand All @@ -80,8 +81,14 @@ export function* getArmToken(
PersistAzureLoginChanged,
persistLogin
);
CommandServiceImpl.remoteCall(TrackEvent, 'signIn_success').catch(
_e => void 0
);
} else {
yield DialogService.showDialog(action.payload.loginFailedDialog);
CommandServiceImpl.remoteCall(TrackEvent, 'signIn_failure').catch(
_e => void 0
);
}
yield put(azureArmTokenDataChanged(azureAuth.access_token));
return azureAuth;
Expand Down
14 changes: 13 additions & 1 deletion packages/app/client/src/data/sagas/resourceSagas.spec.ts
Expand Up @@ -241,7 +241,7 @@ describe('The ResourceSagas', () => {
});
});

describe(',when opening the resource in the Emulator', () => {
describe('when opening the resource in the Emulator', () => {
let mockResource;
beforeEach(() => {
mockResource = BotConfigWithPathImpl.serviceFromJSON({
Expand All @@ -259,6 +259,12 @@ describe('The ResourceSagas', () => {
args: ['the/file/path/chat.chat', true],
},
]);
expect(mockRemoteCommandsCalled).toEqual([
{
commandName: SharedConstants.Commands.Telemetry.TrackEvent,
args: ['chatFile_open'],
},
]);
});

it('should open a transcript file', async () => {
Expand All @@ -270,6 +276,12 @@ describe('The ResourceSagas', () => {
args: ['the/file/path/transcript.transcript'],
},
]);
expect(mockRemoteCommandsCalled).toEqual([
{
commandName: SharedConstants.Commands.Telemetry.TrackEvent,
args: ['transcriptFile_open', { method: 'resources_pane' }],
},
]);
});
});

Expand Down
7 changes: 7 additions & 0 deletions packages/app/client/src/data/sagas/resourcesSagas.ts
Expand Up @@ -129,11 +129,18 @@ function* doOpenResource(
action: ResourcesAction<IFileService>
): IterableIterator<any> {
const { OpenChatFile, OpenTranscript } = SharedConstants.Commands.Emulator;
const { TrackEvent } = SharedConstants.Commands.Telemetry;
const { path } = action.payload;
if (isChatFile(path)) {
yield CommandServiceImpl.call(OpenChatFile, path, true);
CommandServiceImpl.remoteCall(TrackEvent, 'chatFile_open').catch(
_e => void 0
);
} else if (isTranscriptFile(path)) {
yield CommandServiceImpl.call(OpenTranscript, path);
CommandServiceImpl.remoteCall(TrackEvent, 'transcriptFile_open', {
method: 'resources_pane',
}).catch(_e => void 0);
}
// unknown types just fall into the abyss
}
Expand Down
7 changes: 7 additions & 0 deletions packages/app/client/src/hyperlinkHandler.ts
Expand Up @@ -41,16 +41,23 @@ const Electron = (window as any).require('electron');
const { shell } = Electron;

export function navigate(url: string) {
const { TrackEvent } = SharedConstants.Commands.Telemetry;
try {
const parsed = URL.parse(url) || { protocol: '' };
if ((parsed.protocol || '').startsWith('oauth:')) {
navigateEmulatedOAuthUrl(url.substring(8));
} else if (parsed.protocol.startsWith('oauthlink:')) {
navigateOAuthUrl(url.substring(12));
} else {
CommandServiceImpl.remoteCall(TrackEvent, 'app_openLink', { url }).catch(
_e => void 0
);
shell.openExternal(url, { activate: true });
}
} catch (e) {
CommandServiceImpl.remoteCall(TrackEvent, 'app_openLink', { url }).catch(
_e => void 0
);
shell.openExternal(url, { activate: true });
}
}
Expand Down
4 changes: 4 additions & 0 deletions packages/app/client/src/inspector-preload.js
Expand Up @@ -105,6 +105,10 @@ window.host = {
}
},

trackEvent: function(name, properties) {
ipcRenderer.sendToHost('track-event', name, properties);
},

dispatch: function(event, ...args) {
this.handlers[event].forEach(handler => handler(...args));
},
Expand Down