Skip to content

Commit

Permalink
Ability to have telemetry always opted in (#49798)
Browse files Browse the repository at this point in the history
* Initial work

* WIP changes

* Turn off banner when allowChangingOptInStatus is true

* Fix bugs

* Fix broken jest tests

* Add jest tests for TelemetryForm

* Add TelemetryOptIn jest tests

* Make some adjustments to allow always being opted in

* Disallow turning telemetry completely off

* Fix bug in Joi config

* Keep route there
  • Loading branch information
mikecote committed Nov 11, 2019
1 parent a746bce commit ea9bb0a
Show file tree
Hide file tree
Showing 15 changed files with 470 additions and 23 deletions.
52 changes: 47 additions & 5 deletions src/legacy/core_plugins/telemetry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* under the License.
*/

import * as Rx from 'rxjs';
import { resolve } from 'path';
import JoiNamespace from 'joi';
import { Server } from 'hapi';
Expand Down Expand Up @@ -45,6 +46,14 @@ const telemetry = (kibana: any) => {
config(Joi: typeof JoiNamespace) {
return Joi.object({
enabled: Joi.boolean().default(true),
optIn: Joi.when('allowChangingOptInStatus', {
is: false,
then: Joi.valid(true),
otherwise: Joi.boolean()
.allow(null)
.default(null),
}),
allowChangingOptInStatus: Joi.boolean().default(true),
// `config` is used internally and not intended to be set
config: Joi.string().default(Joi.ref('$defaultConfigPath')),
banner: Joi.boolean().default(true),
Expand Down Expand Up @@ -80,8 +89,25 @@ const telemetry = (kibana: any) => {
},
},
async replaceInjectedVars(originalInjectedVars: any, request: any) {
const config = request.server.config();
const optIn = config.get('telemetry.optIn');
const allowChangingOptInStatus = config.get('telemetry.allowChangingOptInStatus');
const currentKibanaVersion = getCurrentKibanaVersion(request.server);
const telemetryOptedIn = await getTelemetryOptIn({ request, currentKibanaVersion });
let telemetryOptedIn: boolean | null;

if (typeof optIn === 'boolean' && !allowChangingOptInStatus) {
// When not allowed to change optIn status and an optIn value is set, we'll overwrite with that
telemetryOptedIn = optIn;
} else {
telemetryOptedIn = await getTelemetryOptIn({
request,
currentKibanaVersion,
});
if (telemetryOptedIn === null) {
// In the senario there's no value set in telemetryOptedIn, we'll return optIn value
telemetryOptedIn = optIn;
}
}

return {
...originalInjectedVars,
Expand All @@ -93,28 +119,44 @@ const telemetry = (kibana: any) => {
return {
telemetryEnabled: getXpackConfigWithDeprecated(config, 'telemetry.enabled'),
telemetryUrl: getXpackConfigWithDeprecated(config, 'telemetry.url'),
telemetryBanner: getXpackConfigWithDeprecated(config, 'telemetry.banner'),
telemetryOptedIn: null,
telemetryBanner:
config.get('telemetry.allowChangingOptInStatus') !== false &&
getXpackConfigWithDeprecated(config, 'telemetry.banner'),
telemetryOptedIn: config.get('telemetry.optIn'),
allowChangingOptInStatus: config.get('telemetry.allowChangingOptInStatus'),
};
},
hacks: ['plugins/telemetry/hacks/telemetry_init', 'plugins/telemetry/hacks/telemetry_opt_in'],
mappings,
},
init(server: Server) {
async init(server: Server) {
const initializerContext = {
env: {
packageInfo: {
version: getCurrentKibanaVersion(server),
},
},
config: {
create() {
const config = server.config();
return Rx.of({
enabled: config.get('telemetry.enabled'),
optIn: config.get('telemetry.optIn'),
config: config.get('telemetry.config'),
banner: config.get('telemetry.banner'),
url: config.get('telemetry.url'),
allowChangingOptInStatus: config.get('telemetry.allowChangingOptInStatus'),
});
},
},
} as PluginInitializerContext;

const coreSetup = ({
http: { server },
log: server.log,
} as any) as CoreSetup;

telemetryPlugin(initializerContext).setup(coreSetup);
await telemetryPlugin(initializerContext).setup(coreSetup);

// register collectors
server.usage.collectorSet.register(createLocalizationUsageCollector(server));
Expand Down

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

Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ export class TelemetryForm extends Component {
queryMatches,
} = this.state;

if (!telemetryOptInProvider.canChangeOptInStatus()) {
return null;
}

if (queryMatches !== null && !queryMatches) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import '../services/telemetry_opt_in.test.mocks';
import { mockInjectedMetadata } from '../services/telemetry_opt_in.test.mocks';
import React from 'react';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import { TelemetryForm } from './telemetry_form';
Expand All @@ -33,6 +33,8 @@ const buildTelemetryOptInProvider = () => {
switch (key) {
case '$http':
return mockHttp;
case 'allowChangingOptInStatus':
return true;
default:
return null;
}
Expand All @@ -47,7 +49,23 @@ const buildTelemetryOptInProvider = () => {
};

describe('TelemetryForm', () => {
it('renders as expected', () => {
it('renders as expected when allows to change optIn status', () => {
mockInjectedMetadata({ telemetryOptedIn: null, allowChangingOptInStatus: true });

expect(shallowWithIntl(
<TelemetryForm
spacesEnabled={false}
query={{ text: '' }}
onQueryMatchChange={jest.fn()}
telemetryOptInProvider={buildTelemetryOptInProvider()}
enableSaving={true}
/>)
).toMatchSnapshot();
});

it(`doesn't render form when not allowed to change optIn status`, () => {
mockInjectedMetadata({ telemetryOptedIn: null, allowChangingOptInStatus: false });

expect(shallowWithIntl(
<TelemetryForm
spacesEnabled={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ describe('click_banner', () => {

const optIn = true;
const bannerId = 'bruce-banner';
mockInjectedMetadata({ telemetryOptedIn: optIn });
mockInjectedMetadata({ telemetryOptedIn: optIn, allowChangingOptInStatus: true });
const telemetryOptInProvider = getTelemetryOptInProvider();

telemetryOptInProvider.setBannerId(bannerId);
Expand All @@ -92,7 +92,7 @@ describe('click_banner', () => {
remove: sinon.spy()
};
const optIn = true;
mockInjectedMetadata({ telemetryOptedIn: null });
mockInjectedMetadata({ telemetryOptedIn: null, allowChangingOptInStatus: true });
const telemetryOptInProvider = getTelemetryOptInProvider({ simulateFailure: true });

await clickBanner(telemetryOptInProvider, optIn, { _banners: banners, _toastNotifications: toastNotifications });
Expand All @@ -110,7 +110,7 @@ describe('click_banner', () => {
remove: sinon.spy()
};
const optIn = false;
mockInjectedMetadata({ telemetryOptedIn: null });
mockInjectedMetadata({ telemetryOptedIn: null, allowChangingOptInStatus: true });
const telemetryOptInProvider = getTelemetryOptInProvider({ simulateError: true });

await clickBanner(telemetryOptInProvider, optIn, { _banners: banners, _toastNotifications: toastNotifications });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const getTelemetryOptInProvider = (enabled, { simulateFailure = false } = {}) =>
const chrome = {
addBasePath: url => url
};
mockInjectedMetadata({ telemetryOptedIn: enabled });
mockInjectedMetadata({ telemetryOptedIn: enabled, allowChangingOptInStatus: true });

const $injector = {
get: (key) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const getMockInjector = () => {
};

const getTelemetryOptInProvider = ({ telemetryOptedIn = null } = {}) => {
mockInjectedMetadata({ telemetryOptedIn });
mockInjectedMetadata({ telemetryOptedIn, allowChangingOptInStatus: true });
const injector = getMockInjector();
const chrome = {
addBasePath: (url) => url
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('TelemetryOptInProvider', () => {
addBasePath: (url) => url
};

mockInjectedMetadata({ telemetryOptedIn: optedIn });
mockInjectedMetadata({ telemetryOptedIn: optedIn, allowChangingOptInStatus: true });

const mockInjector = {
get: (key) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ import {
} from '../../../../../core/public/mocks';
const injectedMetadataMock = injectedMetadataServiceMock.createStartContract();

export function mockInjectedMetadata({ telemetryOptedIn }) {
export function mockInjectedMetadata({ telemetryOptedIn, allowChangingOptInStatus }) {
const mockGetInjectedVar = jest.fn().mockImplementation((key) => {
switch (key) {
case 'telemetryOptedIn': return telemetryOptedIn;
case 'allowChangingOptInStatus': return allowChangingOptInStatus;
default: throw new Error(`unexpected injectedVar ${key}`);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,15 @@ let currentOptInStatus = false;

export function TelemetryOptInProvider($injector: any, chrome: any) {
currentOptInStatus = npStart.core.injectedMetadata.getInjectedVar('telemetryOptedIn') as boolean;
const allowChangingOptInStatus = npStart.core.injectedMetadata.getInjectedVar(
'allowChangingOptInStatus'
) as boolean;

setCanTrackUiMetrics(currentOptInStatus);
const provider = {
getBannerId: () => bannerId,
getOptIn: () => currentOptInStatus,
canChangeOptInStatus: () => allowChangingOptInStatus,
setBannerId(id: string) {
bannerId = id;
},
Expand Down
2 changes: 1 addition & 1 deletion src/legacy/core_plugins/telemetry/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class TelemetryPlugin {
this.currentKibanaVersion = initializerContext.env.packageInfo.version;
}

public setup(core: CoreSetup) {
public async setup(core: CoreSetup) {
const currentKibanaVersion = this.currentKibanaVersion;
telemetryCollectionManager.setStatsGetter(getStats, 'local');
registerRoutes({ core, currentKibanaVersion });
Expand Down
2 changes: 1 addition & 1 deletion src/legacy/core_plugins/telemetry/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ interface RegisterRoutesParams {
}

export function registerRoutes({ core, currentKibanaVersion }: RegisterRoutesParams) {
registerOptInRoutes({ core, currentKibanaVersion });
registerTelemetryDataRoutes(core);
registerOptInRoutes({ core, currentKibanaVersion });
}
Loading

0 comments on commit ea9bb0a

Please sign in to comment.