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

Ability to have telemetry always opted in #49798

Merged
merged 16 commits into from
Nov 11, 2019
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 43 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,10 @@ const telemetry = (kibana: any) => {
config(Joi: typeof JoiNamespace) {
return Joi.object({
enabled: Joi.boolean().default(true),
optIn: 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 +85,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 +115,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
10 changes: 8 additions & 2 deletions src/legacy/core_plugins/telemetry/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,27 @@
* under the License.
*/

import { first } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { CoreSetup, PluginInitializerContext } from 'src/core/server';
import { registerRoutes } from './routes';
import { telemetryCollectionManager } from './collection_manager';
import { getStats } from './telemetry_collection';

export class TelemetryPlugin {
private readonly currentKibanaVersion: string;
private readonly config$: Observable<any>;

constructor(initializerContext: PluginInitializerContext) {
this.currentKibanaVersion = initializerContext.env.packageInfo.version;
this.config$ = initializerContext.config.create();
}

public setup(core: CoreSetup) {
public async setup(core: CoreSetup) {
const currentKibanaVersion = this.currentKibanaVersion;
telemetryCollectionManager.setStatsGetter(getStats, 'local');
registerRoutes({ core, currentKibanaVersion });

const { allowChangingOptInStatus } = await this.config$.pipe(first()).toPromise();
registerRoutes({ allowChangingOptInStatus, core, currentKibanaVersion });
}
}
12 changes: 10 additions & 2 deletions src/legacy/core_plugins/telemetry/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,17 @@ import { registerTelemetryDataRoutes } from './telemetry_stats';
interface RegisterRoutesParams {
core: CoreSetup;
currentKibanaVersion: string;
allowChangingOptInStatus: boolean;
}

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

if (allowChangingOptInStatus) {
registerOptInRoutes({ core, currentKibanaVersion });
}
}
Loading