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] Prepare for versioned HTTP APIs #152111

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/plugins/telemetry/common/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,3 @@
* Fetch Telemetry Config
*/
export const FetchTelemetryConfigRoute = '/api/telemetry/v2/config';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is kind of an interesting one... I am deferring changing this path as I am not familiar with the original context for introducing v2 in it, but I am guessing once an actual versioning mechanism is available we can come back to it? Let me know what y'all think.

Copy link
Member

Choose a reason for hiding this comment

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

That is a very good question!! I need to rely on @Bamieh because the /v2/ part of the telemetry APIs predates me 🤷

FWIW, our telemetry APIs should be considered internal (we probably should name them as such, TBH)... Having said that, we are aware that some external products already use them, so I don't think we can introduce a breaking change without notifying them (or providing a sensible deprecation period).

Copy link
Contributor

Choose a reason for hiding this comment

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

the /v2/ part of the telemetry APIs

From what I can recall, 'v2' deals with encryption

Copy link
Member

Choose a reason for hiding this comment

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

The versioning is introduced for external consumers to check for and detect how to handle incoming requests (our telemetry endpoint for example).

v1 -> v2 was for encryption. This way the telemetry cluster endpoint will check the request URL and will handle the request differently based on the version.

export interface FetchTelemetryConfigResponse {
allowChangingOptInStatus: boolean;
optIn: boolean | null;
sendUsageFrom: 'server' | 'browser';
telemetryNotifyUserAboutOptInDefault: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
* Side Public License, v 1.
*/

export type EncryptedTelemetryPayload = Array<{ clusterUuid: string; stats: string }>;
export type UnencryptedTelemetryPayload = Array<{ clusterUuid: string; stats: object }>;
export * from './latest';

export * as v2 from './v2';
9 changes: 9 additions & 0 deletions src/plugins/telemetry/common/types/latest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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.
*/

export * from './v2';
71 changes: 71 additions & 0 deletions src/plugins/telemetry/common/types/v2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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.
*/

export interface Telemetry {
/** Whether telemetry is enabled */
enabled?: boolean | null;
lastVersionChecked?: string;
/** Whether to send usage from the server or browser. */
sendUsageFrom?: 'browser' | 'server';
lastReported?: number;
allowChangingOptInStatus?: boolean;
userHasSeenNotice?: boolean;
reportFailureCount?: number;
reportFailureVersion?: string;
}

export interface FetchTelemetryConfigResponse {
allowChangingOptInStatus: boolean;
optIn: boolean | null;
sendUsageFrom: 'server' | 'browser';
telemetryNotifyUserAboutOptInDefault: boolean;
}

export interface FetchLastReportedResponse {
lastReported: undefined | number;
}

export type UpdateLastReportedResponse = undefined;

export interface OptInStatsBody {
enabled: boolean;
/** @default true */
unencrypted?: boolean;
}

export interface StatsPayload {
cluster_uuid: string;
opt_in_status: boolean;
}

export type OptInStatsResponse = Array<{
clusterUuid: string;
stats: StatsPayload;
}>;

export interface OptInBody {
enabled: boolean;
}

export type OptInResponse = Array<{
clusterUuid: string;
stats: string;
}>;

export interface UsageStatsBody {
/** @default false */
unencrypted: boolean;
/** @default false */
refreshCache: boolean;
}

export type UseHasSeenNoticeResponse = Telemetry;

export type EncryptedTelemetryPayload = Array<{ clusterUuid: string; stats: string }>;

export type UnencryptedTelemetryPayload = Array<{ clusterUuid: string; stats: object }>;
5 changes: 3 additions & 2 deletions src/plugins/telemetry/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ 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 { FetchTelemetryConfigRoute } from '../common/routes';
import type { v2 } from '../common/types';
import { TelemetrySender, TelemetryService, TelemetryNotifications } from './services';
import { renderWelcomeTelemetryNotice } from './render_welcome_telemetry_notice';

Expand Down Expand Up @@ -322,7 +323,7 @@ export class TelemetryPlugin implements Plugin<TelemetryPluginSetup, TelemetryPl
*/
private async fetchUpdatedConfig(http: HttpStart | HttpSetup): Promise<TelemetryPluginConfig> {
const { allowChangingOptInStatus, optIn, sendUsageFrom, telemetryNotifyUserAboutOptInDefault } =
await http.get<FetchTelemetryConfigResponse>(FetchTelemetryConfigRoute);
await http.get<v2.FetchTelemetryConfigResponse>(FetchTelemetryConfigRoute);

return {
...this.config,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { exhaustMap } from 'rxjs/operators';
import { Storage } from '@kbn/kibana-utils-plugin/public';
import { LOCALSTORAGE_KEY, PAYLOAD_CONTENT_ENCODING } from '../../common/constants';
import { TelemetryService } from './telemetry_service';
import type { EncryptedTelemetryPayload } from '../../common/types';
import type { EncryptedTelemetryPayload } from '../../common/types/latest';
import { isReportIntervalExpired } from '../../common/is_report_interval_expired';

export class TelemetrySender {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import { i18n } from '@kbn/i18n';
import { CoreStart } from '@kbn/core/public';
import { TelemetryPluginConfig } from '../plugin';
import { getTelemetryChannelEndpoint } from '../../common/telemetry_config/get_telemetry_channel_endpoint';
import type { UnencryptedTelemetryPayload, EncryptedTelemetryPayload } from '../../common/types';
import type {
UnencryptedTelemetryPayload,
EncryptedTelemetryPayload,
} from '../../common/types/latest';
import { PAYLOAD_CONTENT_ENCODING } from '../../common/constants';

interface TelemetryServiceConstructor {
Expand Down
5 changes: 3 additions & 2 deletions src/plugins/telemetry/server/routes/telemetry_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
import { type Observable, firstValueFrom } from 'rxjs';
import type { IRouter, SavedObjectsClient } from '@kbn/core/server';
import type { TelemetryConfigType } from '../config';
import { FetchTelemetryConfigResponse, FetchTelemetryConfigRoute } from '../../common/routes';
import { v2 } from '../../common/types';
import { FetchTelemetryConfigRoute } from '../../common/routes';
import { getTelemetrySavedObject } from '../saved_objects';
import {
getNotifyUserAboutOptInDefault,
Expand Down Expand Up @@ -64,7 +65,7 @@ export function registerTelemetryConfigRoutes({
telemetryOptedIn: optIn,
});

const body: FetchTelemetryConfigResponse = {
const body: v2.FetchTelemetryConfigResponse = {
allowChangingOptInStatus,
optIn,
sendUsageFrom,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import type { IRouter, SavedObjectsClient } from '@kbn/core/server';
import type { Observable } from 'rxjs';
import { firstValueFrom } from 'rxjs';
import { v2 } from '../../common/types';
import { getTelemetrySavedObject, updateTelemetrySavedObject } from '../saved_objects';

export function registerTelemetryLastReported(
Expand All @@ -25,10 +26,12 @@ export function registerTelemetryLastReported(
const savedObjectsInternalClient = await firstValueFrom(savedObjectsInternalClient$);
const telemetrySavedObject = await getTelemetrySavedObject(savedObjectsInternalClient);

const body: v2.FetchLastReportedResponse = {
lastReported: telemetrySavedObject && telemetrySavedObject?.lastReported,
};

return res.ok({
body: {
lastReported: telemetrySavedObject && telemetrySavedObject?.lastReported,
},
body,
});
}
);
Expand Down
5 changes: 4 additions & 1 deletion src/plugins/telemetry/server/routes/telemetry_opt_in.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
StatsGetterConfig,
TelemetryCollectionManagerPluginSetup,
} from '@kbn/telemetry-collection-manager-plugin/server';
import { v2 } from '../../common/types';
import { sendTelemetryOptInStatus } from './telemetry_opt_in_stats';
import {
getTelemetrySavedObject,
Expand Down Expand Up @@ -109,7 +110,9 @@ export function registerTelemetryOptInRoutes({
return res.forbidden();
}
}
return res.ok({ body: optInStatus });

const body: v2.OptInResponse = optInStatus;
return res.ok({ body });
}
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
TelemetryCollectionManagerPluginSetup,
StatsGetterConfig,
} from '@kbn/telemetry-collection-manager-plugin/server';
import type { v2 } from '../../common/types';
import { EncryptedTelemetryPayload, UnencryptedTelemetryPayload } from '../../common/types';
import { getTelemetryChannelEndpoint } from '../../common/telemetry_config';
import { PAYLOAD_CONTENT_ENCODING } from '../../common/constants';
Expand Down Expand Up @@ -90,7 +91,8 @@ export function registerTelemetryOptInStatsRoutes(
newOptInStatus,
statsGetterConfig
);
return res.ok({ body: optInStatus });
const body: v2.OptInStatsResponse = optInStatus;
return res.ok({ body });
} catch (err) {
return res.ok({ body: [] });
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
StatsGetterConfig,
} from '@kbn/telemetry-collection-manager-plugin/server';
import type { SecurityPluginStart } from '@kbn/security-plugin/server';
import { v2 } from '../../common/types';

export type SecurityGetter = () => SecurityPluginStart | undefined;

Expand Down Expand Up @@ -64,8 +65,10 @@ export function registerTelemetryUsageStatsRoutes(
refreshCache: unencrypted || refreshCache,
};

const stats = await telemetryCollectionManager.getStats(statsConfig);
return res.ok({ body: stats });
const body: v2.UnencryptedTelemetryPayload = await telemetryCollectionManager.getStats(
statsConfig
);
return res.ok({ body });
} catch (err) {
if (isDev) {
// don't ignore errors when running in dev mode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import type { IRouter } from '@kbn/core/server';
import { TELEMETRY_SAVED_OBJECT_TYPE } from '../saved_objects';
import { v2 } from '../../common/types';
import {
type TelemetrySavedObjectAttributes,
getTelemetrySavedObject,
Expand All @@ -33,7 +34,17 @@ export function registerTelemetryUserHasSeenNotice(router: IRouter) {
};
await updateTelemetrySavedObject(soClient, updatedAttributes);

return res.ok({ body: updatedAttributes });
const body: v2.Telemetry = {
allowChangingOptInStatus: updatedAttributes.allowChangingOptInStatus,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Explicitly map our SO attributes to the response

enabled: updatedAttributes.enabled,
lastReported: updatedAttributes.lastReported,
lastVersionChecked: updatedAttributes.lastVersionChecked,
reportFailureCount: updatedAttributes.reportFailureCount,
reportFailureVersion: updatedAttributes.reportFailureVersion,
sendUsageFrom: updatedAttributes.sendUsageFrom,
userHasSeenNotice: updatedAttributes.userHasSeenNotice,
};
return res.ok({ body });
}
);
}