From ec0230b1cccb4e891feae7687da8fb59b8e80788 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Thu, 29 Aug 2024 17:01:07 +0200 Subject: [PATCH] Dashboard favorites telemetry (#190706) ## Summary Add telemetry to favorites feature https://github.com/elastic/kibana/pull/189285 - Adds UI usage counter telemetry, increase the counter when favorite / unfavorite is clicked - Add snapshot telemetry: - total "favorite" object in the deployment - total users+spaces count combination who have used the favorites feature - avg per user per space (only counts those users who favorited at least once) - max favorites objects per user per space Unfortunately, for snapshot telemetry, I had to add fields to kibana mapping. We didn't need them for a feature, but I didn't realize that will have to add them to a mapping. Not sure if there is a better way --- .../content-management/favorites/README.mdx | 4 +- .../src/components/favorite_button.tsx | 5 + .../favorites_public/src/favorites_client.ts | 16 ++- .../favorites/favorites_public/tsconfig.json | 1 + .../src/favorites_saved_object.ts | 27 +++- .../src/favorites_usage_collection.ts | 129 ++++++++++++++++++ .../favorites/favorites_server/src/index.ts | 17 ++- .../favorites/favorites_server/tsconfig.json | 2 + .../current_fields.json | 6 +- .../current_mappings.json | 12 +- .../check_registered_types.test.ts | 2 +- src/plugins/content_management/kibana.jsonc | 5 +- .../content_management/server/plugin.test.ts | 17 +-- .../content_management/server/plugin.ts | 4 +- .../content_management/server/types.ts | 6 +- src/plugins/content_management/tsconfig.json | 1 + .../dashboard_favorites_service.stub.ts | 4 +- .../dashboard_favorites_service.ts | 7 +- .../telemetry/schema/kbn_packages.json | 32 +++++ src/plugins/telemetry/tsconfig.json | 1 + .../apis/content_management/favorites.ts | 27 ++++ .../apis/telemetry/telemetry.ts | 6 +- .../apis/telemetry/telemetry_local.ts | 6 +- .../common/telemetry/snapshot_telemetry.ts | 6 +- 24 files changed, 317 insertions(+), 26 deletions(-) create mode 100644 packages/content-management/favorites/favorites_server/src/favorites_usage_collection.ts diff --git a/packages/content-management/favorites/README.mdx b/packages/content-management/favorites/README.mdx index e54f1baef2c98..0c5d5167595a6 100644 --- a/packages/content-management/favorites/README.mdx +++ b/packages/content-management/favorites/README.mdx @@ -28,9 +28,11 @@ import { FavoriteButton, } from '@kbn/content-management-favorites-public'; +const appName = 'my-app'; const favoriteObjectType = 'dashboard'; -const favoritesClient = new FavoritesClient('dashboard', { +const favoritesClient = new FavoritesClient(appName, favoriteObjectType, { http: core.http, + usageCollection: plugins.usageCollection, }); // wrap your content with the favorites context provider diff --git a/packages/content-management/favorites/favorites_public/src/components/favorite_button.tsx b/packages/content-management/favorites/favorites_public/src/components/favorite_button.tsx index a82933bd0f53e..19e5e28dd99ad 100644 --- a/packages/content-management/favorites/favorites_public/src/components/favorite_button.tsx +++ b/packages/content-management/favorites/favorites_public/src/components/favorite_button.tsx @@ -12,6 +12,7 @@ import classNames from 'classnames'; import { EuiButtonIcon, euiCanAnimate, EuiThemeComputed } from '@elastic/eui'; import { css } from '@emotion/react'; import { useFavorites, useRemoveFavorite, useAddFavorite } from '../favorites_query'; +import { useFavoritesClient } from '../favorites_context'; export interface FavoriteButtonProps { id: string; @@ -24,6 +25,8 @@ export const FavoriteButton = ({ id, className }: FavoriteButtonProps) => { const removeFavorite = useRemoveFavorite(); const addFavorite = useAddFavorite(); + const favoritesClient = useFavoritesClient(); + if (!data) return null; const isFavorite = data.favoriteIds.includes(id); @@ -40,6 +43,7 @@ export const FavoriteButton = ({ id, className }: FavoriteButtonProps) => { aria-label={title} iconType={'starFilled'} onClick={() => { + favoritesClient?.reportRemoveFavoriteClick(); removeFavorite.mutate({ id }); }} className={classNames(className, 'cm-favorite-button', { @@ -59,6 +63,7 @@ export const FavoriteButton = ({ id, className }: FavoriteButtonProps) => { aria-label={title} iconType={'starEmpty'} onClick={() => { + favoritesClient?.reportAddFavoriteClick(); addFavorite.mutate({ id }); }} className={classNames(className, 'cm-favorite-button', { diff --git a/packages/content-management/favorites/favorites_public/src/favorites_client.ts b/packages/content-management/favorites/favorites_public/src/favorites_client.ts index 58eea3a600de4..c79df94926ed4 100644 --- a/packages/content-management/favorites/favorites_public/src/favorites_client.ts +++ b/packages/content-management/favorites/favorites_public/src/favorites_client.ts @@ -7,6 +7,7 @@ */ import type { HttpStart } from '@kbn/core-http-browser'; +import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; import type { GetFavoritesResponse } from '@kbn/content-management-favorites-server'; export interface FavoritesClientPublic { @@ -15,10 +16,16 @@ export interface FavoritesClientPublic { removeFavorite({ id }: { id: string }): Promise; getFavoriteType(): string; + reportAddFavoriteClick(): void; + reportRemoveFavoriteClick(): void; } export class FavoritesClient implements FavoritesClientPublic { - constructor(private favoriteObjectType: string, private deps: { http: HttpStart }) {} + constructor( + private readonly appName: string, + private readonly favoriteObjectType: string, + private readonly deps: { http: HttpStart; usageCollection?: UsageCollectionStart } + ) {} public async getFavorites(): Promise { return this.deps.http.get(`/internal/content_management/favorites/${this.favoriteObjectType}`); @@ -39,4 +46,11 @@ export class FavoritesClient implements FavoritesClientPublic { public getFavoriteType() { return this.favoriteObjectType; } + + public reportAddFavoriteClick() { + this.deps.usageCollection?.reportUiCounter(this.appName, 'click', 'add_favorite'); + } + public reportRemoveFavoriteClick() { + this.deps.usageCollection?.reportUiCounter(this.appName, 'click', 'remove_favorite'); + } } diff --git a/packages/content-management/favorites/favorites_public/tsconfig.json b/packages/content-management/favorites/favorites_public/tsconfig.json index 2e5ef8bc52347..3057c828fc3da 100644 --- a/packages/content-management/favorites/favorites_public/tsconfig.json +++ b/packages/content-management/favorites/favorites_public/tsconfig.json @@ -22,5 +22,6 @@ "@kbn/core-http-browser", "@kbn/content-management-favorites-server", "@kbn/i18n-react", + "@kbn/usage-collection-plugin", ] } diff --git a/packages/content-management/favorites/favorites_server/src/favorites_saved_object.ts b/packages/content-management/favorites/favorites_server/src/favorites_saved_object.ts index e7d88045860f4..4b283dbef29d7 100644 --- a/packages/content-management/favorites/favorites_server/src/favorites_saved_object.ts +++ b/packages/content-management/favorites/favorites_server/src/favorites_saved_object.ts @@ -21,13 +21,19 @@ const schemaV1 = schema.object({ favoriteIds: schema.arrayOf(schema.string()), }); +export const favoritesSavedObjectName = 'favorites'; + export const favoritesSavedObjectType: SavedObjectsType = { - name: 'favorites', + name: favoritesSavedObjectName, hidden: true, namespaceType: 'single', mappings: { dynamic: false, - properties: {}, + properties: { + userId: { type: 'keyword' }, + type: { type: 'keyword' }, + favoriteIds: { type: 'keyword' }, + }, }, modelVersions: { 1: { @@ -41,5 +47,22 @@ export const favoritesSavedObjectType: SavedObjectsType = { create: schemaV1, }, }, + 2: { + // the model stays the same, but we added the mappings for the snapshot telemetry needs + changes: [ + { + type: 'mappings_addition', + addedMappings: { + userId: { type: 'keyword' }, + type: { type: 'keyword' }, + favoriteIds: { type: 'keyword' }, + }, + }, + ], + schemas: { + forwardCompatibility: schemaV1.extends({}, { unknowns: 'ignore' }), + create: schemaV1, + }, + }, }, }; diff --git a/packages/content-management/favorites/favorites_server/src/favorites_usage_collection.ts b/packages/content-management/favorites/favorites_server/src/favorites_usage_collection.ts new file mode 100644 index 0000000000000..22aab861cc6f0 --- /dev/null +++ b/packages/content-management/favorites/favorites_server/src/favorites_usage_collection.ts @@ -0,0 +1,129 @@ +/* + * 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. + */ + +import type { CoreSetup } from '@kbn/core-lifecycle-server'; +import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; +import type { estypes } from '@elastic/elasticsearch'; +import { favoritesSavedObjectName } from './favorites_saved_object'; + +interface FavoritesUsage { + [favorite_object_type: string]: { + total: number; + total_users_spaces: number; + avg_per_user_per_space: number; + max_per_user_per_space: number; + }; +} + +export function registerFavoritesUsageCollection({ + core, + usageCollection, +}: { + core: CoreSetup; + usageCollection: UsageCollectionSetup; +}) { + usageCollection.registerCollector( + usageCollection.makeUsageCollector({ + type: 'favorites', + isReady: () => true, + schema: { + DYNAMIC_KEY /* e.g. 'dashboard' */: { + total: { + type: 'long', + _meta: { description: 'Total favorite object count in this deployment' }, + }, + total_users_spaces: { + type: 'long', + _meta: { + description: + 'Total users per space that have favorited an object of this type in this deployment', + }, + }, + avg_per_user_per_space: { + type: 'double', + _meta: { + description: + 'Average favorite objects count of this type per user per space for this deployment, only counts users who have favorited at least one object of this type', + }, + }, + max_per_user_per_space: { + type: 'long', + _meta: { + description: + 'Max favorite objects count of this type per user per space for this deployment', + }, + }, + }, + }, + fetch: async (context) => { + const favoritesIndex = await core + .getStartServices() + .then(([{ savedObjects }]) => savedObjects.getIndexForType(favoritesSavedObjectName)); + + const response = await context.esClient.search< + unknown, + { types: estypes.AggregationsStringTermsAggregate } + >({ + index: favoritesIndex, + size: 0, + _source: false, + filter_path: ['aggregations'], + query: { + bool: { + filter: [ + { + term: { + type: 'favorites', + }, + }, + ], + }, + }, + runtime_mappings: { + number_of_favorites: { + type: 'long', + script: { + source: "emit(doc['favorites.favoriteIds'].length)", + }, + }, + }, + aggs: { + types: { + terms: { + field: 'favorites.type', + }, + aggs: { + stats: { + stats: { + field: 'number_of_favorites', + }, + }, + }, + }, + }, + }); + + const favoritesUsage: FavoritesUsage = {}; + + const typesBuckets = (response.aggregations?.types?.buckets ?? + []) as estypes.AggregationsStringTermsBucket[]; + + typesBuckets.forEach((bucket) => { + favoritesUsage[bucket.key] = { + total: bucket.stats.sum, + total_users_spaces: bucket.stats.count, + avg_per_user_per_space: bucket.stats.avg, + max_per_user_per_space: bucket.stats.max, + }; + }); + + return favoritesUsage; + }, + }) + ); +} diff --git a/packages/content-management/favorites/favorites_server/src/index.ts b/packages/content-management/favorites/favorites_server/src/index.ts index 729d4537717c9..c639bd53361d0 100644 --- a/packages/content-management/favorites/favorites_server/src/index.ts +++ b/packages/content-management/favorites/favorites_server/src/index.ts @@ -7,8 +7,10 @@ */ import type { CoreSetup, Logger } from '@kbn/core/server'; +import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; import { registerFavoritesRoutes } from './favorites_routes'; import { favoritesSavedObjectType } from './favorites_saved_object'; +import { registerFavoritesUsageCollection } from './favorites_usage_collection'; export type { GetFavoritesResponse } from './favorites_routes'; @@ -18,8 +20,21 @@ export type { GetFavoritesResponse } from './favorites_routes'; * * @param logger * @param core + * @param usageCollection */ -export function registerFavorites({ logger, core }: { core: CoreSetup; logger: Logger }) { +export function registerFavorites({ + logger, + core, + usageCollection, +}: { + core: CoreSetup; + logger: Logger; + usageCollection?: UsageCollectionSetup; +}) { core.savedObjects.registerType(favoritesSavedObjectType); registerFavoritesRoutes({ core, logger }); + + if (usageCollection) { + registerFavoritesUsageCollection({ core, usageCollection }); + } } diff --git a/packages/content-management/favorites/favorites_server/tsconfig.json b/packages/content-management/favorites/favorites_server/tsconfig.json index fd4fbfb554cf6..5a9ae392c875b 100644 --- a/packages/content-management/favorites/favorites_server/tsconfig.json +++ b/packages/content-management/favorites/favorites_server/tsconfig.json @@ -17,5 +17,7 @@ "@kbn/core", "@kbn/config-schema", "@kbn/core-saved-objects-api-server", + "@kbn/core-lifecycle-server", + "@kbn/usage-collection-plugin", ] } diff --git a/packages/kbn-check-mappings-update-cli/current_fields.json b/packages/kbn-check-mappings-update-cli/current_fields.json index 6138ac9d9eae1..0447ba6a226dd 100644 --- a/packages/kbn-check-mappings-update-cli/current_fields.json +++ b/packages/kbn-check-mappings-update-cli/current_fields.json @@ -436,7 +436,11 @@ "updated_by", "version" ], - "favorites": [], + "favorites": [ + "favoriteIds", + "type", + "userId" + ], "file": [ "FileKind", "Meta", diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index 8098f637b177f..bda2270001bd9 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -1484,7 +1484,17 @@ }, "favorites": { "dynamic": false, - "properties": {} + "properties": { + "favoriteIds": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "userId": { + "type": "keyword" + } + } }, "file": { "dynamic": false, diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 4c9a9fad340c8..de0afdf85ea16 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -98,7 +98,7 @@ describe('checking migration metadata changes on all registered SO types', () => "event_loop_delays_daily": "01b967e8e043801357503de09199dfa3853bab88", "exception-list": "4aebc4e61fb5d608cae48eaeb0977e8db21c61a4", "exception-list-agnostic": "6d3262d58eee28ac381ec9654f93126a58be6f5d", - "favorites": "ef282e9fb5a91df3cc88409a9f86d993fb51a6e9", + "favorites": "a68c7c8ae22eaddcca324d8b3bfc80a94e3eec3a", "file": "6b65ae5899b60ebe08656fd163ea532e557d3c98", "file-upload-usage-collection-telemetry": "06e0a8c04f991e744e09d03ab2bd7f86b2088200", "fileShare": "5be52de1747d249a221b5241af2838264e19aaa1", diff --git a/src/plugins/content_management/kibana.jsonc b/src/plugins/content_management/kibana.jsonc index 73878817d9d20..7ebfe75180658 100644 --- a/src/plugins/content_management/kibana.jsonc +++ b/src/plugins/content_management/kibana.jsonc @@ -6,6 +6,9 @@ "plugin": { "id": "contentManagement", "server": true, - "browser": true + "browser": true, + "optionalPlugins": [ + "usageCollection" + ] } } diff --git a/src/plugins/content_management/server/plugin.test.ts b/src/plugins/content_management/server/plugin.test.ts index d79bb7e827553..4c0e15553b6c8 100644 --- a/src/plugins/content_management/server/plugin.test.ts +++ b/src/plugins/content_management/server/plugin.test.ts @@ -80,14 +80,15 @@ const setup = () => { ...coreSetup, http, }, + pluginsSetup: {}, }; }; describe('ContentManagementPlugin', () => { describe('setup()', () => { test('should expose the core API', () => { - const { plugin, coreSetup } = setup(); - const api = plugin.setup(coreSetup); + const { plugin, coreSetup, pluginsSetup } = setup(); + const api = plugin.setup(coreSetup, pluginsSetup); expect(Object.keys(api).sort()).toEqual(['crud', 'eventBus', 'register']); expect(api.crud('')).toBe('mockedCrud'); @@ -97,8 +98,8 @@ describe('ContentManagementPlugin', () => { describe('RPC', () => { test('should create a rpc POST HTTP route on the router', () => { - const { plugin, coreSetup, router } = setup(); - plugin.setup(coreSetup); + const { plugin, coreSetup, router, pluginsSetup } = setup(); + plugin.setup(coreSetup, pluginsSetup); const [routeConfig]: Parameters = (router.post as jest.Mock).mock.calls[0]; @@ -106,8 +107,8 @@ describe('ContentManagementPlugin', () => { }); test('should register all the procedures in the RPC service and the route handler must send to each procedure the core request context + the request body as input', async () => { - const { plugin, coreSetup, router } = setup(); - plugin.setup(coreSetup); + const { plugin, coreSetup, router, pluginsSetup } = setup(); + plugin.setup(coreSetup, pluginsSetup); const [_, handler]: Parameters = (router.post as jest.Mock).mock.calls[0]; @@ -150,8 +151,8 @@ describe('ContentManagementPlugin', () => { }); test('should return error in custom error format', async () => { - const { plugin, coreSetup, router } = setup(); - plugin.setup(coreSetup); + const { plugin, coreSetup, router, pluginsSetup } = setup(); + plugin.setup(coreSetup, pluginsSetup); const [_, handler]: Parameters = (router.post as jest.Mock).mock.calls[0]; diff --git a/src/plugins/content_management/server/plugin.ts b/src/plugins/content_management/server/plugin.ts index de56805ed50de..db8a15f84a51c 100755 --- a/src/plugins/content_management/server/plugin.ts +++ b/src/plugins/content_management/server/plugin.ts @@ -59,7 +59,7 @@ export class ContentManagementPlugin }); } - public setup(core: CoreSetup) { + public setup(core: CoreSetup, plugins: ContentManagementServerSetupDependencies) { if (this.#eventStream) { this.#eventStream.setup({ core }); } @@ -75,7 +75,7 @@ export class ContentManagementPlugin contentRegistry, }); - registerFavorites({ core, logger: this.logger }); + registerFavorites({ core, logger: this.logger, usageCollection: plugins.usageCollection }); return { ...coreApi, diff --git a/src/plugins/content_management/server/types.ts b/src/plugins/content_management/server/types.ts index dbc401807975d..3209b6616d1c8 100644 --- a/src/plugins/content_management/server/types.ts +++ b/src/plugins/content_management/server/types.ts @@ -7,10 +7,12 @@ */ import type { Version } from '@kbn/object-versioning'; +import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; import type { CoreApi, StorageContextGetTransformFn } from './core'; -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ContentManagementServerSetupDependencies {} +export interface ContentManagementServerSetupDependencies { + usageCollection?: UsageCollectionSetup; +} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ContentManagementServerStartDependencies {} diff --git a/src/plugins/content_management/tsconfig.json b/src/plugins/content_management/tsconfig.json index c23ac37ec58c1..bba887cd887b1 100644 --- a/src/plugins/content_management/tsconfig.json +++ b/src/plugins/content_management/tsconfig.json @@ -17,6 +17,7 @@ "@kbn/saved-objects-settings", "@kbn/core-http-server", "@kbn/content-management-favorites-server", + "@kbn/usage-collection-plugin", "@kbn/object-versioning-utils", ], "exclude": [ diff --git a/src/plugins/dashboard/public/services/dashboard_favorites/dashboard_favorites_service.stub.ts b/src/plugins/dashboard/public/services/dashboard_favorites/dashboard_favorites_service.stub.ts index 57bc91e1fa973..1dba6e25df12c 100644 --- a/src/plugins/dashboard/public/services/dashboard_favorites/dashboard_favorites_service.stub.ts +++ b/src/plugins/dashboard/public/services/dashboard_favorites/dashboard_favorites_service.stub.ts @@ -14,5 +14,7 @@ import { DashboardFavoritesService } from './types'; export type DashboardFavoritesServiceFactory = PluginServiceFactory; export const dashboardFavoritesServiceFactory: DashboardFavoritesServiceFactory = () => { - return new FavoritesClient('dashboard', { http: httpServiceMock.createStartContract() }); + return new FavoritesClient('dashboards', 'dashboard', { + http: httpServiceMock.createStartContract(), + }); }; diff --git a/src/plugins/dashboard/public/services/dashboard_favorites/dashboard_favorites_service.ts b/src/plugins/dashboard/public/services/dashboard_favorites/dashboard_favorites_service.ts index 30342399a7f82..71f986bf4cffa 100644 --- a/src/plugins/dashboard/public/services/dashboard_favorites/dashboard_favorites_service.ts +++ b/src/plugins/dashboard/public/services/dashboard_favorites/dashboard_favorites_service.ts @@ -10,6 +10,7 @@ import { FavoritesClient } from '@kbn/content-management-favorites-public'; import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; import { DashboardStartDependencies } from '../../plugin'; import { DashboardFavoritesService } from './types'; +import { DASHBOARD_APP_ID, DASHBOARD_CONTENT_ID } from '../../dashboard_constants'; export type DashboardFavoritesServiceFactory = KibanaPluginServiceFactory< DashboardFavoritesService, @@ -18,6 +19,10 @@ export type DashboardFavoritesServiceFactory = KibanaPluginServiceFactory< export const dashboardFavoritesServiceFactory: DashboardFavoritesServiceFactory = ({ coreStart, + startPlugins, }) => { - return new FavoritesClient('dashboard', { http: coreStart.http }); + return new FavoritesClient(DASHBOARD_APP_ID, DASHBOARD_CONTENT_ID, { + http: coreStart.http, + usageCollection: startPlugins.usageCollection, + }); }; diff --git a/src/plugins/telemetry/schema/kbn_packages.json b/src/plugins/telemetry/schema/kbn_packages.json index f33b6b66da1f9..7e2c3aad5c59f 100644 --- a/src/plugins/telemetry/schema/kbn_packages.json +++ b/src/plugins/telemetry/schema/kbn_packages.json @@ -1,4 +1,36 @@ { "properties": { + "favorites": { + "properties": { + "DYNAMIC_KEY": { + "properties": { + "total": { + "type": "long", + "_meta": { + "description": "Total favorite object count in this deployment" + } + }, + "total_users_spaces": { + "type": "long", + "_meta": { + "description": "Total users per space that have favorited an object of this type in this deployment" + } + }, + "avg_per_user_per_space": { + "type": "double", + "_meta": { + "description": "Average favorite objects count of this type per user per space for this deployment, only counts users who have favorited at least one object of this type" + } + }, + "max_per_user_per_space": { + "type": "long", + "_meta": { + "description": "Max favorite objects count of this type per user per space for this deployment" + } + } + } + } + } + } } } diff --git a/src/plugins/telemetry/tsconfig.json b/src/plugins/telemetry/tsconfig.json index 2286d13f25e9c..09d5aa25c914b 100644 --- a/src/plugins/telemetry/tsconfig.json +++ b/src/plugins/telemetry/tsconfig.json @@ -11,6 +11,7 @@ "../../../typings/**/*", "schema/oss_plugins.json", "schema/oss_root.json", + "schema/kbn_packages.json" ], "kbn_references": [ "@kbn/core", diff --git a/x-pack/test/api_integration/apis/content_management/favorites.ts b/x-pack/test/api_integration/apis/content_management/favorites.ts index 0fed80620f219..42641f96f63e3 100644 --- a/x-pack/test/api_integration/apis/content_management/favorites.ts +++ b/x-pack/test/api_integration/apis/content_management/favorites.ts @@ -6,6 +6,11 @@ */ import expect from '@kbn/expect'; +import type { UnencryptedTelemetryPayload } from '@kbn/telemetry-plugin/common/types'; +import { + ELASTIC_HTTP_VERSION_HEADER, + X_ELASTIC_INTERNAL_ORIGIN_REQUEST, +} from '@kbn/core-http-common'; import { FtrProviderContext } from '../../ftr_provider_context'; import { @@ -163,6 +168,28 @@ export default function ({ getService }: FtrProviderContext) { response = await api.favorite({ dashboardId: 'fav1', user: interactiveUser3 }); expect(response.body.favoriteIds).to.eql(['fav1']); }); + + // depends on the state from previous test + it('reports favorites stats', async () => { + const { body }: { body: UnencryptedTelemetryPayload } = await getService('supertest') + .post('/internal/telemetry/clusters/_stats') + .set('kbn-xsrf', 'xxx') + .set(ELASTIC_HTTP_VERSION_HEADER, '2') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send({ unencrypted: true, refreshCache: true }) + .expect(200); + + // @ts-ignore + const favoritesStats = body[0].stats.stack_stats.kibana.plugins.favorites; + expect(favoritesStats).to.eql({ + dashboard: { + total: 3, + total_users_spaces: 3, + avg_per_user_per_space: 1, + max_per_user_per_space: 1, + }, + }); + }); }); }); } diff --git a/x-pack/test/api_integration/apis/telemetry/telemetry.ts b/x-pack/test/api_integration/apis/telemetry/telemetry.ts index e0fa1f185c3d2..5e034ce3a1847 100644 --- a/x-pack/test/api_integration/apis/telemetry/telemetry.ts +++ b/x-pack/test/api_integration/apis/telemetry/telemetry.ts @@ -14,6 +14,7 @@ import ossRootTelemetrySchema from '@kbn/telemetry-plugin/schema/oss_root.json'; import xpackRootTelemetrySchema from '@kbn/telemetry-collection-xpack-plugin/schema/xpack_root.json'; import monitoringRootTelemetrySchema from '@kbn/telemetry-collection-xpack-plugin/schema/xpack_monitoring.json'; import ossPluginsTelemetrySchema from '@kbn/telemetry-plugin/schema/oss_plugins.json'; +import ossPackagesTelemetrySchema from '@kbn/telemetry-plugin/schema/kbn_packages.json'; import xpackPluginsTelemetrySchema from '@kbn/telemetry-collection-xpack-plugin/schema/xpack_plugins.json'; import type { UnencryptedTelemetryPayload } from '@kbn/telemetry-plugin/common/types'; import type { @@ -160,7 +161,10 @@ export default function ({ getService }: FtrProviderContext) { // It's nested because of the way it's collected and declared monitoringRootTelemetrySchema.properties.monitoringTelemetry.properties.stats.items ); - const plugins = deepmerge(ossPluginsTelemetrySchema, xpackPluginsTelemetrySchema); + const plugins = deepmerge( + deepmerge(ossPluginsTelemetrySchema, ossPackagesTelemetrySchema), + xpackPluginsTelemetrySchema + ); try { assertTelemetryPayload({ root, plugins }, localXPack); diff --git a/x-pack/test/api_integration/apis/telemetry/telemetry_local.ts b/x-pack/test/api_integration/apis/telemetry/telemetry_local.ts index bc59b02955ba0..ffd37240303fc 100644 --- a/x-pack/test/api_integration/apis/telemetry/telemetry_local.ts +++ b/x-pack/test/api_integration/apis/telemetry/telemetry_local.ts @@ -9,6 +9,7 @@ import expect from '@kbn/expect'; import deepmerge from 'deepmerge'; import ossRootTelemetrySchema from '@kbn/telemetry-plugin/schema/oss_root.json'; import ossPluginsTelemetrySchema from '@kbn/telemetry-plugin/schema/oss_plugins.json'; +import ossPackagesTelemetrySchema from '@kbn/telemetry-plugin/schema/kbn_packages.json'; import xpackRootTelemetrySchema from '@kbn/telemetry-collection-xpack-plugin/schema/xpack_root.json'; import xpackPluginsTelemetrySchema from '@kbn/telemetry-collection-xpack-plugin/schema/xpack_plugins.json'; import { assertTelemetryPayload } from '@kbn/telemetry-tools'; @@ -56,7 +57,10 @@ export default function ({ getService }: FtrProviderContext) { it('should pass the schema validation', () => { const root = deepmerge(ossRootTelemetrySchema, xpackRootTelemetrySchema); - const plugins = deepmerge(ossPluginsTelemetrySchema, xpackPluginsTelemetrySchema); + const plugins = deepmerge( + deepmerge(ossPluginsTelemetrySchema, ossPackagesTelemetrySchema), + xpackPluginsTelemetrySchema + ); try { assertTelemetryPayload({ root, plugins }, stats); diff --git a/x-pack/test_serverless/api_integration/test_suites/common/telemetry/snapshot_telemetry.ts b/x-pack/test_serverless/api_integration/test_suites/common/telemetry/snapshot_telemetry.ts index 08a887e40cc73..b3d7f812de3e8 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/telemetry/snapshot_telemetry.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/telemetry/snapshot_telemetry.ts @@ -10,6 +10,7 @@ import deepmerge from 'deepmerge'; import ossRootTelemetrySchema from '@kbn/telemetry-plugin/schema/oss_root.json'; import xpackRootTelemetrySchema from '@kbn/telemetry-collection-xpack-plugin/schema/xpack_root.json'; import ossPluginsTelemetrySchema from '@kbn/telemetry-plugin/schema/oss_plugins.json'; +import ossPackagesTelemetrySchema from '@kbn/telemetry-plugin/schema/kbn_packages.json'; import xpackPluginsTelemetrySchema from '@kbn/telemetry-collection-xpack-plugin/schema/xpack_plugins.json'; import { assertTelemetryPayload } from '@kbn/telemetry-tools'; import type { UsageStatsPayloadTestFriendly } from '@kbn/test-suites-xpack/api_integration/services/usage_api'; @@ -41,7 +42,10 @@ export default function ({ getService }: FtrProviderContext) { it('should pass the schema validation (ensures BWC with Classic offering)', () => { const root = deepmerge(ossRootTelemetrySchema, xpackRootTelemetrySchema); - const plugins = deepmerge(ossPluginsTelemetrySchema, xpackPluginsTelemetrySchema); + const plugins = deepmerge( + deepmerge(ossPluginsTelemetrySchema, ossPackagesTelemetrySchema), + xpackPluginsTelemetrySchema + ); try { assertTelemetryPayload({ root, plugins }, stats);