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] Set telemetry SO as hidden #147631

Merged
merged 3 commits into from
Dec 20, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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
18 changes: 18 additions & 0 deletions src/plugins/telemetry/common/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

/**
* Fetch Telemetry Config
*/
export const FetchTelemetryConfigRoute = '/api/telemetry/v2/config';
export interface FetchTelemetryConfigResponse {
allowChangingOptInStatus: boolean;
optIn: boolean | null;
sendUsageFrom: 'server' | 'browser';
telemetryNotifyUserAboutOptInDefault: boolean;
}
5 changes: 0 additions & 5 deletions src/plugins/telemetry/common/telemetry_config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@
* Side Public License, v 1.
*/

export { getTelemetryOptIn } from './get_telemetry_opt_in';
export { getTelemetrySendUsageFrom } from './get_telemetry_send_usage_from';
export { getTelemetryAllowChangingOptInStatus } from './get_telemetry_allow_changing_opt_in_status';
export { getTelemetryFailureDetails } from './get_telemetry_failure_details';
export type { TelemetryFailureDetails } from './get_telemetry_failure_details';
export { getTelemetryChannelEndpoint } from './get_telemetry_channel_endpoint';
export type {
GetTelemetryChannelEndpointConfig,
Expand Down
102 changes: 22 additions & 80 deletions src/plugins/telemetry/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,17 @@ import type {
CoreSetup,
HttpStart,
PluginInitializerContext,
SavedObjectsClientContract,
SavedObjectsBatchResponse,
ApplicationStart,
DocLinksStart,
HttpSetup,
} from '@kbn/core/public';
import type { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/public';
import type { HomePublicPluginSetup } from '@kbn/home-plugin/public';
import { ElasticV3BrowserShipper } from '@kbn/analytics-shippers-elastic-v3-browser';

import { of } from 'rxjs';
import { FetchTelemetryConfigResponse, FetchTelemetryConfigRoute } from '../common/routes';
import { TelemetrySender, TelemetryService, TelemetryNotifications } from './services';
import type {
TelemetrySavedObjectAttributes,
TelemetrySavedObject,
} from '../common/telemetry_config/types';
import { getNotifyUserAboutOptInDefault } from '../common/telemetry_config/get_telemetry_notify_user_about_optin_default';
import { renderWelcomeTelemetryNotice } from './render_welcome_telemetry_notice';

/**
Expand Down Expand Up @@ -122,7 +117,6 @@ export class TelemetryPlugin implements Plugin<TelemetryPluginSetup, TelemetryPl
private telemetryNotifications?: TelemetryNotifications;
private telemetryService?: TelemetryService;
private canUserChangeSettings: boolean = true;
private savedObjectsClient?: SavedObjectsClientContract;

constructor(initializerContext: PluginInitializerContext<TelemetryPluginConfig>) {
this.currentKibanaVersion = initializerContext.env.packageInfo.version;
Expand Down Expand Up @@ -169,7 +163,7 @@ export class TelemetryPlugin implements Plugin<TelemetryPluginSetup, TelemetryPl
});

this.telemetrySender = new TelemetrySender(this.telemetryService, async () => {
await this.refreshConfig();
await this.refreshConfig(http);
analytics.optIn({ global: { enabled: this.telemetryService!.isOptedIn } });
});

Expand Down Expand Up @@ -200,7 +194,6 @@ export class TelemetryPlugin implements Plugin<TelemetryPluginSetup, TelemetryPl
overlays,
theme,
application,
savedObjects,
docLinks,
}: CoreStart): TelemetryPluginStart {
if (!this.telemetryService) {
Expand All @@ -220,16 +213,14 @@ export class TelemetryPlugin implements Plugin<TelemetryPluginSetup, TelemetryPl
});
this.telemetryNotifications = telemetryNotifications;

this.savedObjectsClient = savedObjects.client;

application.currentAppId$.subscribe(async () => {
const isUnauthenticated = this.getIsUnauthenticated(http);
if (isUnauthenticated) {
return;
}

// Refresh and get telemetry config
const updatedConfig = await this.refreshConfig();
const updatedConfig = await this.refreshConfig(http);

analytics.optIn({ global: { enabled: this.telemetryService!.isOptedIn } });

Expand Down Expand Up @@ -267,14 +258,17 @@ export class TelemetryPlugin implements Plugin<TelemetryPluginSetup, TelemetryPl
};
}

private async refreshConfig(): Promise<TelemetryPluginConfig | undefined> {
if (this.savedObjectsClient && this.telemetryService) {
// Update the telemetry config based as a mix of the config files and saved objects
const telemetrySavedObject = await this.getTelemetrySavedObject(this.savedObjectsClient);
const updatedConfig = await this.updateConfigsBasedOnSavedObjects(telemetrySavedObject);
/**
* Retrieve the up-to-date configuration
* @param http HTTP helper to make requests to the server
* @private
*/
private async refreshConfig(http: HttpStart | HttpSetup): Promise<TelemetryPluginConfig> {
const updatedConfig = await this.fetchUpdatedConfig(http);
if (this.telemetryService) {
this.telemetryService.config = updatedConfig;
return updatedConfig;
}
return updatedConfig;
}

/**
Expand Down Expand Up @@ -321,74 +315,22 @@ export class TelemetryPlugin implements Plugin<TelemetryPluginSetup, TelemetryPl
}
}

private async updateConfigsBasedOnSavedObjects(
telemetrySavedObject: TelemetrySavedObject
): Promise<TelemetryPluginConfig> {
const configTelemetrySendUsageFrom = this.config.sendUsageFrom;
const configTelemetryOptIn = this.config.optIn as boolean;
const configTelemetryAllowChangingOptInStatus = this.config.allowChangingOptInStatus;

const currentKibanaVersion = this.currentKibanaVersion;

const { getTelemetryAllowChangingOptInStatus, getTelemetryOptIn, getTelemetrySendUsageFrom } =
await import('../common/telemetry_config');

const allowChangingOptInStatus = getTelemetryAllowChangingOptInStatus({
configTelemetryAllowChangingOptInStatus,
telemetrySavedObject,
});

const optIn = getTelemetryOptIn({
configTelemetryOptIn,
allowChangingOptInStatus,
telemetrySavedObject,
currentKibanaVersion,
});

const sendUsageFrom = getTelemetrySendUsageFrom({
configTelemetrySendUsageFrom,
telemetrySavedObject,
});

const telemetryNotifyUserAboutOptInDefault = getNotifyUserAboutOptInDefault({
telemetrySavedObject,
allowChangingOptInStatus,
configTelemetryOptIn,
telemetryOptedIn: optIn,
});
/**
* Fetch configuration from the server and merge it with the one the browser already knows
* @param http The HTTP helper to make the requests
* @private
*/
private async fetchUpdatedConfig(http: HttpStart | HttpSetup): Promise<TelemetryPluginConfig> {
const { allowChangingOptInStatus, optIn, sendUsageFrom, telemetryNotifyUserAboutOptInDefault } =
await http.get<FetchTelemetryConfigResponse>(FetchTelemetryConfigRoute);

return {
...this.config,
allowChangingOptInStatus,
optIn,
sendUsageFrom,
telemetryNotifyUserAboutOptInDefault,
userCanChangeSettings: this.canUserChangeSettings,
};
}

private async getTelemetrySavedObject(savedObjectsClient: SavedObjectsClientContract) {
try {
// Use bulk get API here to avoid the queue. This could fail independent requests if we don't have rights to access the telemetry object otherwise
const {
savedObjects: [{ attributes }],
} = (await savedObjectsClient.bulkGet([
{
id: 'telemetry',
type: 'telemetry',
},
])) as SavedObjectsBatchResponse<TelemetrySavedObjectAttributes>;
return attributes;
} catch (error) {
const errorCode = error[Symbol('SavedObjectsClientErrorCode')];
if (errorCode === 'SavedObjectsClient/notFound') {
return null;
}

if (errorCode === 'SavedObjectsClient/forbidden') {
return false;
}

throw error;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
*/

import { Observable, firstValueFrom } from 'rxjs';
import { ISavedObjectsRepository, SavedObjectsClient } from '@kbn/core/server';
import { ISavedObjectsRepository } from '@kbn/core/server';
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server';
import { getTelemetrySavedObject, TelemetrySavedObject } from '../../telemetry_repository';
import { getTelemetryOptIn, getTelemetrySendUsageFrom } from '../../../common/telemetry_config';
import { getTelemetrySavedObject, TelemetrySavedObject } from '../../saved_objects';
import { TelemetryConfigType } from '../../config';
import { getTelemetryOptIn, getTelemetrySendUsageFrom } from '../../telemetry_config';

export interface TelemetryUsageStats {
opt_in_status?: boolean | null;
Expand Down Expand Up @@ -39,9 +39,7 @@ export function createCollectorFetch({

try {
const internalRepository = getSavedObjectsClient()!;
telemetrySavedObject = await getTelemetrySavedObject(
new SavedObjectsClient(internalRepository)
);
telemetrySavedObject = await getTelemetrySavedObject(internalRepository);
} catch (err) {
// no-op
}
Expand Down
24 changes: 15 additions & 9 deletions src/plugins/telemetry/server/fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,27 @@ import {
import fetch from 'node-fetch';
import type { TelemetryCollectionManagerPluginStart } from '@kbn/telemetry-collection-manager-plugin/server';
import {
PluginInitializerContext,
Logger,
SavedObjectsClientContract,
type PluginInitializerContext,
type Logger,
type SavedObjectsClientContract,
SavedObjectsClient,
CoreStart,
type CoreStart,
} from '@kbn/core/server';
import { getTelemetryChannelEndpoint } from '../common/telemetry_config';
import {
TELEMETRY_SAVED_OBJECT_TYPE,
getTelemetrySavedObject,
updateTelemetrySavedObject,
} from './saved_objects';
import { getNextAttemptDate } from './get_next_attempt_date';
import {
getTelemetryChannelEndpoint,
getTelemetryOptIn,
getTelemetrySendUsageFrom,
getTelemetryFailureDetails,
} from '../common/telemetry_config';
import { getTelemetrySavedObject, updateTelemetrySavedObject } from './telemetry_repository';
} from './telemetry_config';
import { PAYLOAD_CONTENT_ENCODING } from '../common/constants';
import type { EncryptedTelemetryPayload } from '../common/types';
import { TelemetryConfigType } from './config';
import type { TelemetryConfigType } from './config';
import { isReportIntervalExpired } from '../common/is_report_interval_expired';

export interface FetcherTaskDepsStart {
Expand Down Expand Up @@ -78,7 +82,9 @@ export class FetcherTask {
}

public start({ savedObjects }: CoreStart, { telemetryCollectionManager }: FetcherTaskDepsStart) {
this.internalRepository = new SavedObjectsClient(savedObjects.createInternalRepository());
this.internalRepository = new SavedObjectsClient(
savedObjects.createInternalRepository([TELEMETRY_SAVED_OBJECT_TYPE])
);
Comment on lines +85 to +87
Copy link
Member Author

Choose a reason for hiding this comment

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

nit: Potential improvement: Probably using the repository is good enough (no need to instantiate the client).

Copy link
Contributor

@TinaHeiligers TinaHeiligers Dec 18, 2022

Choose a reason for hiding this comment

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

ATM, hidden types aren't exposed to the SO client unless we instantiate a new client exposing the hidden types with includedHiddenTypes (ref: #147150). So yes, we don't need to create a new client (unless I'm missing something)

this.telemetryCollectionManager = telemetryCollectionManager;

this.subscriptions.add(this.validateConnectivity());
Expand Down
Loading