diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts index c0e39c2a1d9b3..400e5c7f97969 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts @@ -48,8 +48,7 @@ const commonSchema: MakeSchemaFrom = { }, }; -// These keys obtained by searching for `/application\w*\.register\(/` and checking the value of the attr `id`. -// TODO: Find a way to update these keys automatically. +// There is a test in x-pack/test/usage_collection that validates that the keys in here match all the registered apps export const applicationUsageSchema = { // OSS dashboards: commonSchema, @@ -61,10 +60,12 @@ export const applicationUsageSchema = { short_url_redirect: commonSchema, // It's a forward app so we'll likely never report it timelion: commonSchema, visualize: commonSchema, + error: commonSchema, + status: commonSchema, + kibanaOverview: commonSchema, // X-Pack apm: commonSchema, - csm: commonSchema, canvas: commonSchema, dashboard_mode: commonSchema, // It's a forward app so we'll likely never report it enterpriseSearch: commonSchema, @@ -75,6 +76,7 @@ export const applicationUsageSchema = { metrics: commonSchema, infra: commonSchema, // It's a forward app so we'll likely never report it fleet: commonSchema, + ingestManager: commonSchema, lens: commonSchema, maps: commonSchema, ml: commonSchema, @@ -98,4 +100,5 @@ export const applicationUsageSchema = { siem: commonSchema, space_selector: commonSchema, uptime: commonSchema, + ux: commonSchema, }; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index e1c369dc003bd..bed142f165d64 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -719,7 +719,7 @@ } } }, - "apm": { + "error": { "properties": { "appId": { "type": "keyword" @@ -790,7 +790,149 @@ } } }, - "csm": { + "status": { + "properties": { + "appId": { + "type": "keyword" + }, + "viewId": { + "type": "keyword" + }, + "clicks_total": { + "type": "long" + }, + "clicks_7_days": { + "type": "long" + }, + "clicks_30_days": { + "type": "long" + }, + "clicks_90_days": { + "type": "long" + }, + "minutes_on_screen_total": { + "type": "float" + }, + "minutes_on_screen_7_days": { + "type": "float" + }, + "minutes_on_screen_30_days": { + "type": "float" + }, + "minutes_on_screen_90_days": { + "type": "float" + }, + "views": { + "type": "array", + "items": { + "properties": { + "appId": { + "type": "keyword" + }, + "viewId": { + "type": "keyword" + }, + "clicks_total": { + "type": "long" + }, + "clicks_7_days": { + "type": "long" + }, + "clicks_30_days": { + "type": "long" + }, + "clicks_90_days": { + "type": "long" + }, + "minutes_on_screen_total": { + "type": "float" + }, + "minutes_on_screen_7_days": { + "type": "float" + }, + "minutes_on_screen_30_days": { + "type": "float" + }, + "minutes_on_screen_90_days": { + "type": "float" + } + } + } + } + } + }, + "kibanaOverview": { + "properties": { + "appId": { + "type": "keyword" + }, + "viewId": { + "type": "keyword" + }, + "clicks_total": { + "type": "long" + }, + "clicks_7_days": { + "type": "long" + }, + "clicks_30_days": { + "type": "long" + }, + "clicks_90_days": { + "type": "long" + }, + "minutes_on_screen_total": { + "type": "float" + }, + "minutes_on_screen_7_days": { + "type": "float" + }, + "minutes_on_screen_30_days": { + "type": "float" + }, + "minutes_on_screen_90_days": { + "type": "float" + }, + "views": { + "type": "array", + "items": { + "properties": { + "appId": { + "type": "keyword" + }, + "viewId": { + "type": "keyword" + }, + "clicks_total": { + "type": "long" + }, + "clicks_7_days": { + "type": "long" + }, + "clicks_30_days": { + "type": "long" + }, + "clicks_90_days": { + "type": "long" + }, + "minutes_on_screen_total": { + "type": "float" + }, + "minutes_on_screen_7_days": { + "type": "float" + }, + "minutes_on_screen_30_days": { + "type": "float" + }, + "minutes_on_screen_90_days": { + "type": "float" + } + } + } + } + } + }, + "apm": { "properties": { "appId": { "type": "keyword" @@ -1571,6 +1713,77 @@ } } }, + "ingestManager": { + "properties": { + "appId": { + "type": "keyword" + }, + "viewId": { + "type": "keyword" + }, + "clicks_total": { + "type": "long" + }, + "clicks_7_days": { + "type": "long" + }, + "clicks_30_days": { + "type": "long" + }, + "clicks_90_days": { + "type": "long" + }, + "minutes_on_screen_total": { + "type": "float" + }, + "minutes_on_screen_7_days": { + "type": "float" + }, + "minutes_on_screen_30_days": { + "type": "float" + }, + "minutes_on_screen_90_days": { + "type": "float" + }, + "views": { + "type": "array", + "items": { + "properties": { + "appId": { + "type": "keyword" + }, + "viewId": { + "type": "keyword" + }, + "clicks_total": { + "type": "long" + }, + "clicks_7_days": { + "type": "long" + }, + "clicks_30_days": { + "type": "long" + }, + "clicks_90_days": { + "type": "long" + }, + "minutes_on_screen_total": { + "type": "float" + }, + "minutes_on_screen_7_days": { + "type": "float" + }, + "minutes_on_screen_30_days": { + "type": "float" + }, + "minutes_on_screen_90_days": { + "type": "float" + } + } + } + } + } + }, "lens": { "properties": { "appId": { @@ -3203,6 +3416,77 @@ } } } + }, + "ux": { + "properties": { + "appId": { + "type": "keyword" + }, + "viewId": { + "type": "keyword" + }, + "clicks_total": { + "type": "long" + }, + "clicks_7_days": { + "type": "long" + }, + "clicks_30_days": { + "type": "long" + }, + "clicks_90_days": { + "type": "long" + }, + "minutes_on_screen_total": { + "type": "float" + }, + "minutes_on_screen_7_days": { + "type": "float" + }, + "minutes_on_screen_30_days": { + "type": "float" + }, + "minutes_on_screen_90_days": { + "type": "float" + }, + "views": { + "type": "array", + "items": { + "properties": { + "appId": { + "type": "keyword" + }, + "viewId": { + "type": "keyword" + }, + "clicks_total": { + "type": "long" + }, + "clicks_7_days": { + "type": "long" + }, + "clicks_30_days": { + "type": "long" + }, + "clicks_90_days": { + "type": "long" + }, + "minutes_on_screen_total": { + "type": "float" + }, + "minutes_on_screen_7_days": { + "type": "float" + }, + "minutes_on_screen_30_days": { + "type": "float" + }, + "minutes_on_screen_90_days": { + "type": "float" + } + } + } + } + } } } }, diff --git a/test/scripts/jenkins_xpack_build_plugins.sh b/test/scripts/jenkins_xpack_build_plugins.sh index 289e64f66c89b..732743fe804fa 100755 --- a/test/scripts/jenkins_xpack_build_plugins.sh +++ b/test/scripts/jenkins_xpack_build_plugins.sh @@ -11,5 +11,6 @@ node scripts/build_kibana_platform_plugins \ --scan-dir "$XPACK_DIR/test/plugin_api_integration/plugins" \ --scan-dir "$XPACK_DIR/test/plugin_api_perf/plugins" \ --scan-dir "$XPACK_DIR/test/licensing_plugin/plugins" \ + --scan-dir "$XPACK_DIR/test/usage_collection/plugins" \ --workers 12 \ --verbose diff --git a/x-pack/scripts/functional_tests.js b/x-pack/scripts/functional_tests.js index 1a60f479ee07e..5d3afae92abd5 100644 --- a/x-pack/scripts/functional_tests.js +++ b/x-pack/scripts/functional_tests.js @@ -75,4 +75,5 @@ require('@kbn/test').runTestsCli([ require.resolve('../test/saved_object_tagging/functional/config.ts'), require.resolve('../test/saved_object_tagging/api_integration/security_and_spaces/config.ts'), require.resolve('../test/saved_object_tagging/api_integration/tagging_api/config.ts'), + require.resolve('../test/usage_collection/config.ts'), ]); diff --git a/x-pack/test/usage_collection/config.ts b/x-pack/test/usage_collection/config.ts new file mode 100644 index 0000000000000..27b12a1ff298c --- /dev/null +++ b/x-pack/test/usage_collection/config.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { resolve } from 'path'; +import fs from 'fs'; +import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; +import { services } from './services'; +import { pageObjects } from './page_objects'; + +// the default export of config files must be a config provider +// that returns an object with the projects config values + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const xpackFunctionalConfig = await readConfigFile(require.resolve('../functional/config.js')); + + // Find all folders in ./plugins since we treat all them as plugin folder + const allFiles = fs.readdirSync(resolve(__dirname, 'plugins')); + const plugins = allFiles.filter((file) => + fs.statSync(resolve(__dirname, 'plugins', file)).isDirectory() + ); + + return { + ...xpackFunctionalConfig.getAll(), + // list paths to the files that contain your plugins tests + testFiles: [resolve(__dirname, './test_suites/application_usage')], + + services, + pageObjects, + + kbnTestServer: { + ...xpackFunctionalConfig.get('kbnTestServer'), + serverArgs: [ + ...xpackFunctionalConfig.get('kbnTestServer.serverArgs'), + ...plugins.map((pluginDir) => `--plugin-path=${resolve(__dirname, 'plugins', pluginDir)}`), + ], + }, + + junit: { + reportName: 'X-Pack Usage Collection Functional Tests', + }, + }; +} diff --git a/x-pack/test/usage_collection/ftr_provider_context.d.ts b/x-pack/test/usage_collection/ftr_provider_context.d.ts new file mode 100644 index 0000000000000..271f313d4bda9 --- /dev/null +++ b/x-pack/test/usage_collection/ftr_provider_context.d.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { GenericFtrProviderContext } from '@kbn/test/types/ftr'; +import { services } from './services'; +import { pageObjects } from './page_objects'; + +export type FtrProviderContext = GenericFtrProviderContext; diff --git a/x-pack/test/usage_collection/page_objects.ts b/x-pack/test/usage_collection/page_objects.ts new file mode 100644 index 0000000000000..a216b0f2cd47a --- /dev/null +++ b/x-pack/test/usage_collection/page_objects.ts @@ -0,0 +1,6 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export { pageObjects } from '../functional/page_objects'; diff --git a/x-pack/test/usage_collection/plugins/application_usage_test/kibana.json b/x-pack/test/usage_collection/plugins/application_usage_test/kibana.json new file mode 100644 index 0000000000000..1bff6cb64be1c --- /dev/null +++ b/x-pack/test/usage_collection/plugins/application_usage_test/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "applicationUsageTest", + "version": "1.0.0", + "kibanaVersion": "kibana", + "configPath": ["xpack", "applicationUsageTest"], + "requiredPlugins": [], + "server": false, + "ui": true +} diff --git a/x-pack/test/usage_collection/plugins/application_usage_test/public/index.ts b/x-pack/test/usage_collection/plugins/application_usage_test/public/index.ts new file mode 100644 index 0000000000000..83e72770884b1 --- /dev/null +++ b/x-pack/test/usage_collection/plugins/application_usage_test/public/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ApplicationUsageTest } from './plugin'; + +export function plugin() { + return new ApplicationUsageTest(); +} diff --git a/x-pack/test/usage_collection/plugins/application_usage_test/public/plugin.ts b/x-pack/test/usage_collection/plugins/application_usage_test/public/plugin.ts new file mode 100644 index 0000000000000..5df673d94da19 --- /dev/null +++ b/x-pack/test/usage_collection/plugins/application_usage_test/public/plugin.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Plugin, CoreSetup, CoreStart } from 'kibana/public'; +import { first } from 'rxjs/operators'; +import './types'; + +export class ApplicationUsageTest implements Plugin { + public setup(core: CoreSetup) {} + + public async start(core: CoreStart) { + const applications = await core.application.applications$.pipe(first()).toPromise(); + window.__applicationIds__ = [...applications.keys()]; + } +} diff --git a/x-pack/test/usage_collection/plugins/application_usage_test/public/types.ts b/x-pack/test/usage_collection/plugins/application_usage_test/public/types.ts new file mode 100644 index 0000000000000..5282b2fbdeb52 --- /dev/null +++ b/x-pack/test/usage_collection/plugins/application_usage_test/public/types.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export {}; // Hack to declare this file as a module so TS allows us to extend the Global Window interface + +declare global { + interface Window { + __applicationIds__: string[]; + } +} diff --git a/x-pack/test/usage_collection/plugins/application_usage_test/tsconfig.json b/x-pack/test/usage_collection/plugins/application_usage_test/tsconfig.json new file mode 100644 index 0000000000000..f1bf94a38de8f --- /dev/null +++ b/x-pack/test/usage_collection/plugins/application_usage_test/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "public/**/*.ts", + "public/**/*.tsx", + ], + "exclude": [] +} diff --git a/x-pack/test/usage_collection/services.ts b/x-pack/test/usage_collection/services.ts new file mode 100644 index 0000000000000..5c807720b2867 --- /dev/null +++ b/x-pack/test/usage_collection/services.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { services } from '../functional/services'; diff --git a/x-pack/test/usage_collection/test_suites/application_usage/index.ts b/x-pack/test/usage_collection/test_suites/application_usage/index.ts new file mode 100644 index 0000000000000..84a9dccc397cc --- /dev/null +++ b/x-pack/test/usage_collection/test_suites/application_usage/index.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { applicationUsageSchema } from '../../../../../src/plugins/kibana_usage_collection/server/collectors/application_usage/schema'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + describe('Application Usage', function () { + this.tags('ciGroup1'); + const { common } = getPageObjects(['common']); + const browser = getService('browser'); + + it('keys in the schema match the registered application IDs', async () => { + await common.navigateToApp('home'); // Navigate to Home to make sure all the appIds are loaded + const appIds = await browser.execute(() => window.__applicationIds__); + try { + expect(Object.keys(applicationUsageSchema).sort()).to.eql(appIds.sort()); + } catch (err) { + err.message = `Application Usage's schema is not up-to-date with the actual registered apps. Please update it at src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts.\n${err.message}`; + throw err; + } + }); + }); +}