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

[telemetry] expose getIsOptedIn function in plugin start contract #75143

Merged
51 changes: 51 additions & 0 deletions src/plugins/telemetry/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,54 @@ Telemetry allows Kibana features to have usage tracked in the wild. The general
3. Viewing usage data in the Kibana instance of the telemetry cluster (Viewing).

This plugin is responsible for sending usage data to the telemetry cluster. For collecting usage data, use the [`usageCollection` plugin](../usage_collection/README.md)

## Telemetry Plugin public API

### Setup

The `setup` function does not expose any public api.

### Start

The `start` function exposes the following interface:

- `async getIsOptedIn(): Promise<boolean>`:
An async function that resolves into `true` if the user has opted into send Elastic usage data.
Resolves to `false` if the user explicitly opted out of sending usage data to Elastic or did not choose
to opt-in or out yet after an minor or major upgrade.
Bamieh marked this conversation as resolved.
Show resolved Hide resolved


### Usage

To use the exposed plugin start and setup contracts:

1. Make sure `telemetry` is in your `optionalPlugins` in the `kibana.json` file:

```json5
// <plugin>/kibana.json
{
"id": "...",
"optionalPlugins": ["telemetry"]
}
```

2. Use the exposed contracts:
```ts
// <plugin>/server/plugin.ts

import { TelemetryPluginsStart } from '../telemetry/server`;

interface MyPlyginStartDeps {
telemetry?: TelemetryPluginsStart;
}

class MyPlugin {
public async start(
core: CoreStart,
{ telemetry }: MyPlyginStartDeps
) {
const isOptedIn = await telemetry?.getIsOptedIn();
...
}
}
```
2 changes: 1 addition & 1 deletion src/plugins/telemetry/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { configSchema, TelemetryConfigType } from './config';

export { FetcherTask } from './fetcher';
export { handleOldSettings } from './handle_old_settings';
export { TelemetryPluginsSetup } from './plugin';
export { TelemetryPluginsSetup, TelemetryPluginsStart } from './plugin';

export const config: PluginConfigDescriptor<TelemetryConfigType> = {
schema: configSchema,
Expand Down
47 changes: 41 additions & 6 deletions src/plugins/telemetry/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
TelemetryCollectionManagerPluginSetup,
TelemetryCollectionManagerPluginStart,
} from 'src/plugins/telemetry_collection_manager/server';
import { take } from 'rxjs/operators';
import {
CoreSetup,
PluginInitializerContext,
Expand All @@ -42,19 +43,31 @@ import {
import { TelemetryConfigType } from './config';
import { FetcherTask } from './fetcher';
import { handleOldSettings } from './handle_old_settings';
import { getTelemetrySavedObject } from './telemetry_repository';
import { getTelemetryOptIn } from '../common/telemetry_config';

export interface TelemetryPluginsSetup {
interface TelemetryPluginsDepsSetup {
usageCollection: UsageCollectionSetup;
telemetryCollectionManager: TelemetryCollectionManagerPluginSetup;
}

export interface TelemetryPluginsStart {
interface TelemetryPluginsDepsStart {
telemetryCollectionManager: TelemetryCollectionManagerPluginStart;
}

export type TelemetryPluginsSetup = void;
export interface TelemetryPluginsStart {
/**
* Resolves `true` if the user has opted into send Elastic usage data.
* Resolves `false` if the user explicitly opted out of sending usage data to Elastic
* or did not choose to opt-in or out -yet- after an minor or major upgrade.
Bamieh marked this conversation as resolved.
Show resolved Hide resolved
*/
getIsOptedIn: () => Promise<boolean>;
}

type SavedObjectsRegisterType = CoreSetup['savedObjects']['registerType'];

export class TelemetryPlugin implements Plugin {
export class TelemetryPlugin implements Plugin<TelemetryPluginsSetup, TelemetryPluginsStart> {
private readonly logger: Logger;
private readonly currentKibanaVersion: string;
private readonly config$: Observable<TelemetryConfigType>;
Expand All @@ -76,8 +89,8 @@ export class TelemetryPlugin implements Plugin {

public async setup(
{ elasticsearch, http, savedObjects }: CoreSetup,
{ usageCollection, telemetryCollectionManager }: TelemetryPluginsSetup
) {
{ usageCollection, telemetryCollectionManager }: TelemetryPluginsDepsSetup
): Promise<TelemetryPluginsSetup> {
const currentKibanaVersion = this.currentKibanaVersion;
const config$ = this.config$;
const isDev = this.isDev;
Expand All @@ -97,7 +110,10 @@ export class TelemetryPlugin implements Plugin {
this.registerUsageCollectors(usageCollection);
}

public async start(core: CoreStart, { telemetryCollectionManager }: TelemetryPluginsStart) {
public async start(
core: CoreStart,
{ telemetryCollectionManager }: TelemetryPluginsDepsStart
): Promise<TelemetryPluginsStart> {
const { savedObjects, uiSettings } = core;
this.savedObjectsClient = savedObjects.createInternalRepository();
const savedObjectsClient = new SavedObjectsClient(this.savedObjectsClient);
Expand All @@ -110,6 +126,25 @@ export class TelemetryPlugin implements Plugin {
}

this.fetcherTask.start(core, { telemetryCollectionManager });

return {
getIsOptedIn: async () => {
const internalRepository = new SavedObjectsClient(savedObjects.createInternalRepository());
const telemetrySavedObject = await getTelemetrySavedObject(internalRepository!);
const config = await this.config$.pipe(take(1)).toPromise();
const allowChangingOptInStatus = config.allowChangingOptInStatus;
const configTelemetryOptIn = typeof config.optIn === 'undefined' ? null : config.optIn;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: I recently learned this can be also written as:

Suggested change
const configTelemetryOptIn = typeof config.optIn === 'undefined' ? null : config.optIn;
const configTelemetryOptIn = config.optIn ?? null;

🤯 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator

const currentKibanaVersion = this.currentKibanaVersion;
const isOptedIn = getTelemetryOptIn({
currentKibanaVersion,
telemetrySavedObject,
allowChangingOptInStatus,
configTelemetryOptIn,
});

return isOptedIn === true;
},
};
}

private registerMappings(registerType: SavedObjectsRegisterType) {
Expand Down