Skip to content

Commit

Permalink
feat: telemetry api (#4968)
Browse files Browse the repository at this point in the history
* feat: telemetry api

* lint

* persist machine id

* move telemetry settings to user settings

* reverted changes to en-US

* pool telemetry events

* changed batch size

* fix uuid

* add telemetry classes

* requested changes

* shorted interval

* fix integration test

* change parameter type

Co-authored-by: Andy Brown <asbrown002@gmail.com>
  • Loading branch information
tdurnford and a-b-r-o-w-n committed Dec 3, 2020
1 parent 59f7f11 commit 2133a73
Show file tree
Hide file tree
Showing 36 changed files with 681 additions and 189 deletions.
1 change: 1 addition & 0 deletions Composer/cypress/integration/ToDoBot.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

context('ToDo Bot', () => {
before(() => {
window.localStorage.setItem('composer:userSettings', JSON.stringify({ telemetry: { allowDataCollection: false } }));
cy.visit('/home');
cy.createBot('TodoSample');
cy.findByTestId('WelcomeModalCloseIcon').click();
Expand Down
10 changes: 1 addition & 9 deletions Composer/cypress/support/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import axios from 'axios';

import './commands';

beforeEach(() => {
cy.exec('yarn test:integration:clean');
window.localStorage.setItem('composer:userSettings', JSON.stringify({ telemetry: { allowDataCollection: false } }));
window.localStorage.setItem('composer:OnboardingState', JSON.stringify({ complete: true }));
window.sessionStorage.setItem('composer:ProjectIdCache', '');
cy.request('post', '/api/settings', {
settings: {
telemetry: {
allowDataCollection: false,
},
},
});
});

after(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ describe('<CreationFlow/>', () => {
setCreationFlowStatus: jest.fn(),
navTo: jest.fn(),
saveTemplateId: jest.fn(),
setCurrentPageMode: jest.fn(),
});
set(creationFlowStatusState, CreationFlowStatus.NEW_FROM_TEMPLATE);
set(featureFlagsState, getDefaultFeatureFlags());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ describe('<AppSettings /> & <ElectronSettings />', () => {
propertyEditorWidth: 400,
dialogNavWidth: 180,
appLocale: 'en-US',
telemetry: {},
});
});
getByText('Auto update');
Expand Down
6 changes: 4 additions & 2 deletions Composer/packages/client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import { MainContainer } from './components/AppComponents/MainContainer';
import { userSettingsState } from './recoilModel';
import { loadLocale } from './utils/fileUtil';
import { dispatcherState } from './recoilModel/DispatcherWrapper';
import { useInitializeLogger } from './telemetry/useInitializeLogger';

initializeIcons(undefined, { disableWarnings: true });

export const App: React.FC = () => {
const { appLocale } = useRecoilValue(userSettingsState);
const { fetchExtensions, fetchFeatureFlags, fetchServerSettings } = useRecoilValue(dispatcherState);
const { fetchExtensions, fetchFeatureFlags } = useRecoilValue(dispatcherState);

useEffect(() => {
loadLocale(appLocale);
Expand All @@ -25,9 +26,10 @@ export const App: React.FC = () => {
useEffect(() => {
fetchExtensions();
fetchFeatureFlags();
fetchServerSettings();
}, []);

useInitializeLogger();

return (
<Fragment key={appLocale}>
<Announcement />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ import { Suspense, Fragment } from 'react';
import React from 'react';

import { isElectron } from './../../utils/electronUtil';
import { ServerSettingsState, onboardingState } from './../../recoilModel';
import { userSettingsState, onboardingState } from './../../recoilModel';

const Onboarding = React.lazy(() => import('./../../Onboarding/Onboarding'));
const AppUpdater = React.lazy(() => import('./../AppUpdater').then((module) => ({ default: module.AppUpdater })));
const DataCollectionDialog = React.lazy(() => import('./../DataCollectionDialog'));

export const Assistant = () => {
const { telemetry } = useRecoilValue(ServerSettingsState);
const { telemetry } = useRecoilValue(userSettingsState);
const onboarding = useRecoilValue(onboardingState);
const renderAppUpdater = isElectron();

const renderDataCollectionDialog = typeof telemetry?.allowDataCollection === 'undefined';
const renderDataCollectionDialog = typeof telemetry.allowDataCollection === 'undefined';
const renderOnboarding = !renderDataCollectionDialog && !onboarding.complete;

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import { useRecoilValue } from 'recoil';
import { dispatcherState } from '../recoilModel';

const DataCollectionDialog: React.FC = () => {
const { updateServerSettings } = useRecoilValue(dispatcherState);
const { updateUserSettings } = useRecoilValue(dispatcherState);

const handleDataCollectionChange = (allowDataCollection: boolean) => () => {
updateServerSettings({
updateUserSettings({
telemetry: {
allowDataCollection,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { RouteComponentProps } from '@reach/router';
import { useRecoilValue } from 'recoil';

import { isElectron } from '../../../utils/electronUtil';
import { onboardingState, userSettingsState, dispatcherState, ServerSettingsState } from '../../../recoilModel';
import { onboardingState, userSettingsState, dispatcherState } from '../../../recoilModel';

import { container, section } from './styles';
import { SettingToggle } from './SettingToggle';
Expand All @@ -28,8 +28,7 @@ const ElectronSettings = lazy(() =>
const AppSettings: React.FC<RouteComponentProps> = () => {
const [calloutIsShown, showCallout] = useState(false);

const { onboardingSetComplete, updateUserSettings, updateServerSettings } = useRecoilValue(dispatcherState);
const { telemetry } = useRecoilValue(ServerSettingsState);
const { onboardingSetComplete, updateUserSettings } = useRecoilValue(dispatcherState);
const userSettings = useRecoilValue(userSettingsState);
const { complete } = useRecoilValue(onboardingState);
const onOnboardingChange = useCallback(
Expand All @@ -49,8 +48,8 @@ const AppSettings: React.FC<RouteComponentProps> = () => {
updateUserSettings({ appLocale });
};

const handleDataCollectionChange = (allowDataCollection) => {
updateServerSettings({
const handleDataCollectionChange = (allowDataCollection: boolean) => {
updateUserSettings({
telemetry: {
allowDataCollection,
},
Expand Down Expand Up @@ -204,7 +203,7 @@ const AppSettings: React.FC<RouteComponentProps> = () => {
<section css={section}>
<h2>{formatMessage('Data Collection')}</h2>
<SettingToggle
checked={!!telemetry?.allowDataCollection}
checked={!!userSettings.telemetry.allowDataCollection}
description={formatMessage(
'Composer includes a telemetry feature that collects usage information. It is important that the Composer team understands how the tool is being used so that it can be improved.'
)}
Expand Down
11 changes: 1 addition & 10 deletions Composer/packages/client/src/recoilModel/atoms/appState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT License.

import { atom, atomFamily } from 'recoil';
import { FormDialogSchemaTemplate, FeatureFlagMap, BotTemplate, UserSettings, ServerSettings } from '@bfc/shared';
import { FormDialogSchemaTemplate, FeatureFlagMap, BotTemplate, UserSettings } from '@bfc/shared';
import { ExtensionMetadata } from '@bfc/extension-client';
import formatMessage from 'format-message';

Expand Down Expand Up @@ -246,15 +246,6 @@ export const pageElementState = atom<{ [page in PageMode]?: { [key: string]: any
},
});

export const ServerSettingsState = atom<ServerSettings>({
key: getFullyQualifiedKey('serverSettings'),
default: {
telemetry: {
allowDataCollection: false,
},
},
});

export const showCreateDialogModalState = atom<boolean>({
key: getFullyQualifiedKey('showCreateDialogModal'),
default: false,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ describe('user dispatcher', () => {
propertyEditorWidth: 400,
dialogNavWidth: 555,
appLocale: 'en-US',
telemetry: {},
});
});

Expand Down
2 changes: 0 additions & 2 deletions Composer/packages/client/src/recoilModel/dispatchers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import { formDialogsDispatcher } from './formDialogs';
import { botProjectFileDispatcher } from './botProjectFile';
import { zoomDispatcher } from './zoom';
import { recognizerDispatcher } from './recognizers';
import { serverSettingsDispatcher } from './serverSettings';

const createDispatchers = () => {
return {
Expand Down Expand Up @@ -53,7 +52,6 @@ const createDispatchers = () => {
...botProjectFileDispatcher(),
...zoomDispatcher(),
...recognizerDispatcher(),
...serverSettingsDispatcher(),
};
};

Expand Down

This file was deleted.

12 changes: 11 additions & 1 deletion Composer/packages/client/src/recoilModel/dispatchers/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@

import { CallbackInterface, useRecoilCallback } from 'recoil';
import jwtDecode from 'jwt-decode';
import pick from 'lodash/pick';

import { userSettingsState, currentUserState, CurrentUser } from '../atoms/appState';
import { getUserTokenFromCache, loginPopup, refreshToken } from '../../utils/auth';
import storage from '../../utils/storage';
import { loadLocale } from '../../utils/fileUtil';
import { UserSettingsPayload } from '../types';
import { isElectron } from '../../utils/electronUtil';
import httpClient from '../../utils/httpUtil';

import { logMessage } from './shared';

enum ClaimNames {
upn = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn',
Expand Down Expand Up @@ -72,7 +76,8 @@ export const userDispatcher = () => {
});

const updateUserSettings = useRecoilCallback(
({ set }: CallbackInterface) => async (settings: Partial<UserSettingsPayload>) => {
(callbackHelpers: CallbackInterface) => async (settings: Partial<UserSettingsPayload>) => {
const { set } = callbackHelpers;
if (settings.appLocale != null) {
await loadLocale(settings.appLocale);
}
Expand All @@ -91,6 +96,11 @@ export const userDispatcher = () => {
}
storage.set('userSettings', newSettings);

// push telemetry settings to the server
httpClient.post('/settings', { settings: pick(newSettings, ['telemetry']) }).catch((error) => {
logMessage(callbackHelpers, `Error updating server settings: ${error}`);
});

if (isElectron()) {
// push the settings to the electron main process
window.ipcRenderer.send('update-user-settings', newSettings);
Expand Down
Loading

0 comments on commit 2133a73

Please sign in to comment.