diff --git a/.telemetryrc.json b/.telemetryrc.json index 2f57566159a7..818f9805628e 100644 --- a/.telemetryrc.json +++ b/.telemetryrc.json @@ -7,7 +7,6 @@ "src/plugins/testbed/", "src/plugins/kibana_utils/", "src/plugins/kibana_usage_collection/server/collectors/kibana/kibana_usage_collector.ts", - "src/plugins/kibana_usage_collection/server/collectors/application_usage/telemetry_application_usage_collector.ts", "src/plugins/kibana_usage_collection/server/collectors/management/telemetry_management_collector.ts", "src/plugins/kibana_usage_collection/server/collectors/ui_metric/telemetry_ui_metric_collector.ts", "src/plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts" diff --git a/packages/kbn-telemetry-tools/src/tools/__fixture__/mock_schema.json b/packages/kbn-telemetry-tools/src/tools/__fixture__/mock_schema.json index e87699825b4e..2e69d3625d7f 100644 --- a/packages/kbn-telemetry-tools/src/tools/__fixture__/mock_schema.json +++ b/packages/kbn-telemetry-tools/src/tools/__fixture__/mock_schema.json @@ -5,6 +5,22 @@ "flat": { "type": "keyword" }, + "my_index_signature_prop": { + "properties": { + "avg": { + "type": "number" + }, + "count": { + "type": "number" + }, + "max": { + "type": "number" + }, + "min": { + "type": "number" + } + } + }, "my_str": { "type": "text" }, diff --git a/packages/kbn-telemetry-tools/src/tools/__fixture__/parsed_indexed_interface_with_not_matching_schema.ts b/packages/kbn-telemetry-tools/src/tools/__fixture__/parsed_indexed_interface_with_not_matching_schema.ts new file mode 100644 index 000000000000..83866a2b6afe --- /dev/null +++ b/packages/kbn-telemetry-tools/src/tools/__fixture__/parsed_indexed_interface_with_not_matching_schema.ts @@ -0,0 +1,54 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SyntaxKind } from 'typescript'; +import { ParsedUsageCollection } from '../ts_parser'; + +export const parsedIndexedInterfaceWithNoMatchingSchema: ParsedUsageCollection = [ + 'src/fixtures/telemetry_collectors/indexed_interface_with_not_matching_schema.ts', + { + collectorName: 'indexed_interface_with_not_matching_schema', + schema: { + value: { + something: { + count_1: { + type: 'number', + }, + }, + }, + }, + fetch: { + typeName: 'Usage', + typeDescriptor: { + '': { + '@@INDEX@@': { + count_1: { + kind: SyntaxKind.NumberKeyword, + type: 'NumberKeyword', + }, + count_2: { + kind: SyntaxKind.NumberKeyword, + type: 'NumberKeyword', + }, + }, + }, + }, + }, + }, +]; diff --git a/packages/kbn-telemetry-tools/src/tools/__fixture__/parsed_working_collector.ts b/packages/kbn-telemetry-tools/src/tools/__fixture__/parsed_working_collector.ts index 803bc7f13f59..b238c5aa346a 100644 --- a/packages/kbn-telemetry-tools/src/tools/__fixture__/parsed_working_collector.ts +++ b/packages/kbn-telemetry-tools/src/tools/__fixture__/parsed_working_collector.ts @@ -32,6 +32,20 @@ export const parsedWorkingCollector: ParsedUsageCollection = [ my_str: { type: 'text', }, + my_index_signature_prop: { + avg: { + type: 'number', + }, + count: { + type: 'number', + }, + max: { + type: 'number', + }, + min: { + type: 'number', + }, + }, my_objects: { total: { type: 'number', @@ -60,6 +74,14 @@ export const parsedWorkingCollector: ParsedUsageCollection = [ kind: SyntaxKind.StringKeyword, type: 'StringKeyword', }, + my_index_signature_prop: { + '': { + '@@INDEX@@': { + kind: SyntaxKind.NumberKeyword, + type: 'NumberKeyword', + }, + }, + }, my_objects: { total: { kind: SyntaxKind.NumberKeyword, diff --git a/packages/kbn-telemetry-tools/src/tools/__snapshots__/extract_collectors.test.ts.snap b/packages/kbn-telemetry-tools/src/tools/__snapshots__/extract_collectors.test.ts.snap index fc933b6c7fd3..bf1d5ffc1101 100644 --- a/packages/kbn-telemetry-tools/src/tools/__snapshots__/extract_collectors.test.ts.snap +++ b/packages/kbn-telemetry-tools/src/tools/__snapshots__/extract_collectors.test.ts.snap @@ -90,6 +90,38 @@ Array [ }, }, ], + Array [ + "src/fixtures/telemetry_collectors/indexed_interface_with_not_matching_schema.ts", + Object { + "collectorName": "indexed_interface_with_not_matching_schema", + "fetch": Object { + "typeDescriptor": Object { + "": Object { + "@@INDEX@@": Object { + "count_1": Object { + "kind": 140, + "type": "NumberKeyword", + }, + "count_2": Object { + "kind": 140, + "type": "NumberKeyword", + }, + }, + }, + }, + "typeName": "Usage", + }, + "schema": Object { + "value": Object { + "something": Object { + "count_1": Object { + "type": "long", + }, + }, + }, + }, + }, + ], Array [ "src/fixtures/telemetry_collectors/nested_collector.ts", Object { @@ -132,6 +164,14 @@ Array [ "type": "BooleanKeyword", }, }, + "my_index_signature_prop": Object { + "": Object { + "@@INDEX@@": Object { + "kind": 140, + "type": "NumberKeyword", + }, + }, + }, "my_objects": Object { "total": Object { "kind": 140, @@ -166,6 +206,20 @@ Array [ "type": "boolean", }, }, + "my_index_signature_prop": Object { + "avg": Object { + "type": "number", + }, + "count": Object { + "type": "number", + }, + "max": Object { + "type": "number", + }, + "min": Object { + "type": "number", + }, + }, "my_objects": Object { "total": Object { "type": "number", diff --git a/packages/kbn-telemetry-tools/src/tools/check_collector__integrity.test.ts b/packages/kbn-telemetry-tools/src/tools/check_collector__integrity.test.ts index dbdda3f38afd..a101210185a6 100644 --- a/packages/kbn-telemetry-tools/src/tools/check_collector__integrity.test.ts +++ b/packages/kbn-telemetry-tools/src/tools/check_collector__integrity.test.ts @@ -20,6 +20,7 @@ import { cloneDeep } from 'lodash'; import * as ts from 'typescript'; import { parsedWorkingCollector } from './__fixture__/parsed_working_collector'; +import { parsedIndexedInterfaceWithNoMatchingSchema } from './__fixture__/parsed_indexed_interface_with_not_matching_schema'; import { checkCompatibleTypeDescriptor, checkMatchingMapping } from './check_collector_integrity'; import * as path from 'path'; import { readFile } from 'fs'; @@ -82,6 +83,20 @@ describe('checkCompatibleTypeDescriptor', () => { expect(incompatibles).toHaveLength(0); }); + it('returns diff on indexed interface with no matching schema', () => { + const incompatibles = checkCompatibleTypeDescriptor([ + parsedIndexedInterfaceWithNoMatchingSchema, + ]); + expect(incompatibles).toHaveLength(1); + const { diff, message } = incompatibles[0]; + // eslint-disable-next-line @typescript-eslint/naming-convention + expect(diff).toEqual({ '.@@INDEX@@.count_2.kind': 'number' }); + expect(message).toHaveLength(1); + expect(message).toEqual([ + 'incompatible Type key (Usage..@@INDEX@@.count_2): expected (undefined) got ("number").', + ]); + }); + describe('Interface Change', () => { it('returns diff on incompatible type descriptor with mapping', () => { const malformedParsedCollector = cloneDeep(parsedWorkingCollector); diff --git a/packages/kbn-telemetry-tools/src/tools/extract_collectors.test.ts b/packages/kbn-telemetry-tools/src/tools/extract_collectors.test.ts index 1b4ed21a1635..0517cb9034d0 100644 --- a/packages/kbn-telemetry-tools/src/tools/extract_collectors.test.ts +++ b/packages/kbn-telemetry-tools/src/tools/extract_collectors.test.ts @@ -34,7 +34,7 @@ describe('extractCollectors', () => { const programPaths = await getProgramPaths(configs[0]); const results = [...extractCollectors(programPaths, tsConfig)]; - expect(results).toHaveLength(6); + expect(results).toHaveLength(7); expect(results).toMatchSnapshot(); }); }); diff --git a/packages/kbn-telemetry-tools/src/tools/serializer.ts b/packages/kbn-telemetry-tools/src/tools/serializer.ts index bce5dd7f5864..f945402ec5fc 100644 --- a/packages/kbn-telemetry-tools/src/tools/serializer.ts +++ b/packages/kbn-telemetry-tools/src/tools/serializer.ts @@ -84,6 +84,11 @@ export function getDescriptor(node: ts.Node, program: ts.Program): Descriptor | }, {} as any); } + // If it's defined as signature { [key: string]: OtherInterface } + if (ts.isIndexSignatureDeclaration(node) && node.type) { + return { '@@INDEX@@': getDescriptor(node.type, program) }; + } + if (ts.SyntaxKind.FirstNode === node.kind) { return getDescriptor((node as any).right, program); } diff --git a/packages/kbn-telemetry-tools/src/tools/utils.ts b/packages/kbn-telemetry-tools/src/tools/utils.ts index 212b06a4c989..c1424785b22a 100644 --- a/packages/kbn-telemetry-tools/src/tools/utils.ts +++ b/packages/kbn-telemetry-tools/src/tools/utils.ts @@ -98,6 +98,14 @@ export function getVariableValue(node: ts.Node): string | Record { return serializeObject(node); } + if (ts.isIdentifier(node)) { + const declaration = getIdentifierDeclaration(node); + if (ts.isVariableDeclaration(declaration) && declaration.initializer) { + return getVariableValue(declaration.initializer); + } + // TODO: If this is another imported value from another file, we'll need to go fetch it like in getPropertyValue + } + throw Error(`Unsuppored Node: cannot get value of node (${node.getText()}) of kind ${node.kind}`); } @@ -112,10 +120,11 @@ export function serializeObject(node: ts.Node) { if (typeof propertyName === 'undefined') { throw new Error(`Unable to get property name ${property.getText()}`); } + const cleanPropertyName = propertyName.replace(/["']/g, ''); if (ts.isPropertyAssignment(property)) { - value[propertyName] = getVariableValue(property.initializer); + value[cleanPropertyName] = getVariableValue(property.initializer); } else { - value[propertyName] = getVariableValue(property); + value[cleanPropertyName] = getVariableValue(property); } } @@ -222,9 +231,29 @@ export const flattenKeys = (obj: any, keyPath: any[] = []): any => { }; export function difference(actual: any, expected: any) { - function changes(obj: any, base: any) { + function changes(obj: { [key: string]: any }, base: { [key: string]: any }) { return transform(obj, function (result, value, key) { - if (key && !isEqual(value, base[key])) { + if (key && /@@INDEX@@/.test(`${key}`)) { + // The type definition is an Index Signature, fuzzy searching for similar keys + const regexp = new RegExp(`${key}`.replace(/@@INDEX@@/g, '(.+)?')); + const keysInBase = Object.keys(base) + .map((k) => { + const match = k.match(regexp); + return match && match[0]; + }) + .filter((s): s is string => !!s); + + if (keysInBase.length === 0) { + // Mark this key as wrong because we couldn't find any matching keys + result[key] = value; + } + + keysInBase.forEach((k) => { + if (!isEqual(value, base[k])) { + result[k] = isObject(value) && isObject(base[k]) ? changes(value, base[k]) : value; + } + }); + } else if (key && !isEqual(value, base[key])) { result[key] = isObject(value) && isObject(base[key]) ? changes(value, base[key]) : value; } }); diff --git a/src/fixtures/telemetry_collectors/indexed_interface_with_not_matching_schema.ts b/src/fixtures/telemetry_collectors/indexed_interface_with_not_matching_schema.ts new file mode 100644 index 000000000000..0ec8d2e15c34 --- /dev/null +++ b/src/fixtures/telemetry_collectors/indexed_interface_with_not_matching_schema.ts @@ -0,0 +1,48 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { CollectorSet } from '../../plugins/usage_collection/server/collector'; +import { loggerMock } from '../../core/server/logging/logger.mock'; + +const { makeUsageCollector } = new CollectorSet({ + logger: loggerMock.create(), + maximumWaitTimeForAllCollectorsInS: 0, +}); + +interface Usage { + [key: string]: { + count_1?: number; + count_2?: number; + }; +} + +export const myCollector = makeUsageCollector({ + type: 'indexed_interface_with_not_matching_schema', + isReady: () => true, + fetch() { + if (Math.random()) { + return { something: { count_1: 1 } }; + } + return { something: { count_2: 2 } }; + }, + schema: { + something: { + count_1: { type: 'long' }, // Intentionally missing count_2 + }, + }, +}); diff --git a/src/fixtures/telemetry_collectors/working_collector.ts b/src/fixtures/telemetry_collectors/working_collector.ts index d58a89db97d7..bdf10b5e5491 100644 --- a/src/fixtures/telemetry_collectors/working_collector.ts +++ b/src/fixtures/telemetry_collectors/working_collector.ts @@ -35,6 +35,9 @@ interface Usage { my_objects: MyObject; my_array?: MyObject[]; my_str_array?: string[]; + my_index_signature_prop?: { + [key: string]: number; + }; } const SOME_NUMBER: number = 123; @@ -93,5 +96,11 @@ export const myCollector = makeUsageCollector({ type: { type: 'boolean' }, }, my_str_array: { type: 'keyword' }, + my_index_signature_prop: { + count: { type: 'number' }, + avg: { type: 'number' }, + max: { type: 'number' }, + min: { type: 'number' }, + }, }, }); 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 new file mode 100644 index 000000000000..6efe87255358 --- /dev/null +++ b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts @@ -0,0 +1,99 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; +import { ApplicationUsageTelemetryReport } from './telemetry_application_usage_collector'; + +const commonSchema: MakeSchemaFrom = { + 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', + }, +}; + +// 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. +export const applicationUsageSchema = { + // OSS + dashboards: commonSchema, + dev_tools: commonSchema, + discover: commonSchema, + home: commonSchema, + kibana: commonSchema, // It's a forward app so we'll likely never report it + management: commonSchema, + short_url_redirect: commonSchema, // It's a forward app so we'll likely never report it + timelion: commonSchema, + visualize: commonSchema, + + // X-Pack + apm: commonSchema, + csm: commonSchema, + canvas: commonSchema, + dashboard_mode: commonSchema, // It's a forward app so we'll likely never report it + appSearch: commonSchema, + workplaceSearch: commonSchema, + graph: commonSchema, + logs: commonSchema, + metrics: commonSchema, + infra: commonSchema, // It's a forward app so we'll likely never report it + ingestManager: commonSchema, + lens: commonSchema, + maps: commonSchema, + ml: commonSchema, + monitoring: commonSchema, + 'observability-overview': commonSchema, + security_account: commonSchema, + security_access_agreement: commonSchema, + security_capture_url: commonSchema, // It's a forward app so we'll likely never report it + security_logged_out: commonSchema, + security_login: commonSchema, + security_logout: commonSchema, + security_overwritten_session: commonSchema, + securitySolution: commonSchema, // It's a forward app so we'll likely never report it + 'securitySolution:overview': commonSchema, + 'securitySolution:detections': commonSchema, + 'securitySolution:hosts': commonSchema, + 'securitySolution:network': commonSchema, + 'securitySolution:timelines': commonSchema, + 'securitySolution:case': commonSchema, + 'securitySolution:administration': commonSchema, + siem: commonSchema, + space_selector: commonSchema, + uptime: commonSchema, +}; diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/telemetry_application_usage_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/application_usage/telemetry_application_usage_collector.ts index 1f22ab010010..69137681e059 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/telemetry_application_usage_collector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/application_usage/telemetry_application_usage_collector.ts @@ -26,6 +26,7 @@ import { ApplicationUsageTransactional, registerMappings, } from './saved_objects_types'; +import { applicationUsageSchema } from './schema'; /** * Roll indices every 24h @@ -40,7 +41,7 @@ export const ROLL_INDICES_START = 5 * 60 * 1000; export const SAVED_OBJECTS_TOTAL_TYPE = 'application_usage_totals'; export const SAVED_OBJECTS_TRANSACTIONAL_TYPE = 'application_usage_transactional'; -interface ApplicationUsageTelemetryReport { +export interface ApplicationUsageTelemetryReport { [appId: string]: { clicks_total: number; clicks_7_days: number; @@ -60,93 +61,96 @@ export function registerApplicationUsageCollector( ) { registerMappings(registerType); - const collector = usageCollection.makeUsageCollector({ - type: 'application_usage', - isReady: () => typeof getSavedObjectsClient() !== 'undefined', - fetch: async () => { - const savedObjectsClient = getSavedObjectsClient(); - if (typeof savedObjectsClient === 'undefined') { - return; - } - const [rawApplicationUsageTotals, rawApplicationUsageTransactional] = await Promise.all([ - findAll(savedObjectsClient, { type: SAVED_OBJECTS_TOTAL_TYPE }), - findAll(savedObjectsClient, { - type: SAVED_OBJECTS_TRANSACTIONAL_TYPE, - }), - ]); + const collector = usageCollection.makeUsageCollector( + { + type: 'application_usage', + isReady: () => typeof getSavedObjectsClient() !== 'undefined', + schema: applicationUsageSchema, + fetch: async () => { + const savedObjectsClient = getSavedObjectsClient(); + if (typeof savedObjectsClient === 'undefined') { + return; + } + const [rawApplicationUsageTotals, rawApplicationUsageTransactional] = await Promise.all([ + findAll(savedObjectsClient, { type: SAVED_OBJECTS_TOTAL_TYPE }), + findAll(savedObjectsClient, { + type: SAVED_OBJECTS_TRANSACTIONAL_TYPE, + }), + ]); - const applicationUsageFromTotals = rawApplicationUsageTotals.reduce( - (acc, { attributes: { appId, minutesOnScreen, numberOfClicks } }) => { - const existing = acc[appId] || { clicks_total: 0, minutes_on_screen_total: 0 }; - return { - ...acc, - [appId]: { - clicks_total: numberOfClicks + existing.clicks_total, + const applicationUsageFromTotals = rawApplicationUsageTotals.reduce( + (acc, { attributes: { appId, minutesOnScreen, numberOfClicks } }) => { + const existing = acc[appId] || { clicks_total: 0, minutes_on_screen_total: 0 }; + return { + ...acc, + [appId]: { + clicks_total: numberOfClicks + existing.clicks_total, + clicks_7_days: 0, + clicks_30_days: 0, + clicks_90_days: 0, + minutes_on_screen_total: minutesOnScreen + existing.minutes_on_screen_total, + minutes_on_screen_7_days: 0, + minutes_on_screen_30_days: 0, + minutes_on_screen_90_days: 0, + }, + }; + }, + {} as ApplicationUsageTelemetryReport + ); + const nowMinus7 = moment().subtract(7, 'days'); + const nowMinus30 = moment().subtract(30, 'days'); + const nowMinus90 = moment().subtract(90, 'days'); + + const applicationUsage = rawApplicationUsageTransactional.reduce( + (acc, { attributes: { appId, minutesOnScreen, numberOfClicks, timestamp } }) => { + const existing = acc[appId] || { + clicks_total: 0, clicks_7_days: 0, clicks_30_days: 0, clicks_90_days: 0, - minutes_on_screen_total: minutesOnScreen + existing.minutes_on_screen_total, + minutes_on_screen_total: 0, minutes_on_screen_7_days: 0, minutes_on_screen_30_days: 0, minutes_on_screen_90_days: 0, - }, - }; - }, - {} as ApplicationUsageTelemetryReport - ); - const nowMinus7 = moment().subtract(7, 'days'); - const nowMinus30 = moment().subtract(30, 'days'); - const nowMinus90 = moment().subtract(90, 'days'); + }; - const applicationUsage = rawApplicationUsageTransactional.reduce( - (acc, { attributes: { appId, minutesOnScreen, numberOfClicks, timestamp } }) => { - const existing = acc[appId] || { - clicks_total: 0, - clicks_7_days: 0, - clicks_30_days: 0, - clicks_90_days: 0, - minutes_on_screen_total: 0, - minutes_on_screen_7_days: 0, - minutes_on_screen_30_days: 0, - minutes_on_screen_90_days: 0, - }; + const timeOfEntry = moment(timestamp as string); + const isInLast7Days = timeOfEntry.isSameOrAfter(nowMinus7); + const isInLast30Days = timeOfEntry.isSameOrAfter(nowMinus30); + const isInLast90Days = timeOfEntry.isSameOrAfter(nowMinus90); - const timeOfEntry = moment(timestamp as string); - const isInLast7Days = timeOfEntry.isSameOrAfter(nowMinus7); - const isInLast30Days = timeOfEntry.isSameOrAfter(nowMinus30); - const isInLast90Days = timeOfEntry.isSameOrAfter(nowMinus90); + const last7Days = { + clicks_7_days: existing.clicks_7_days + numberOfClicks, + minutes_on_screen_7_days: existing.minutes_on_screen_7_days + minutesOnScreen, + }; + const last30Days = { + clicks_30_days: existing.clicks_30_days + numberOfClicks, + minutes_on_screen_30_days: existing.minutes_on_screen_30_days + minutesOnScreen, + }; + const last90Days = { + clicks_90_days: existing.clicks_90_days + numberOfClicks, + minutes_on_screen_90_days: existing.minutes_on_screen_90_days + minutesOnScreen, + }; - const last7Days = { - clicks_7_days: existing.clicks_7_days + numberOfClicks, - minutes_on_screen_7_days: existing.minutes_on_screen_7_days + minutesOnScreen, - }; - const last30Days = { - clicks_30_days: existing.clicks_30_days + numberOfClicks, - minutes_on_screen_30_days: existing.minutes_on_screen_30_days + minutesOnScreen, - }; - const last90Days = { - clicks_90_days: existing.clicks_90_days + numberOfClicks, - minutes_on_screen_90_days: existing.minutes_on_screen_90_days + minutesOnScreen, - }; + return { + ...acc, + [appId]: { + ...existing, + clicks_total: existing.clicks_total + numberOfClicks, + minutes_on_screen_total: existing.minutes_on_screen_total + minutesOnScreen, + ...(isInLast7Days ? last7Days : {}), + ...(isInLast30Days ? last30Days : {}), + ...(isInLast90Days ? last90Days : {}), + }, + }; + }, + applicationUsageFromTotals + ); - return { - ...acc, - [appId]: { - ...existing, - clicks_total: existing.clicks_total + numberOfClicks, - minutes_on_screen_total: existing.minutes_on_screen_total + minutesOnScreen, - ...(isInLast7Days ? last7Days : {}), - ...(isInLast30Days ? last30Days : {}), - ...(isInLast90Days ? last90Days : {}), - }, - }; - }, - applicationUsageFromTotals - ); - - return applicationUsage; - }, - }); + return applicationUsage; + }, + } + ); usageCollection.registerCollector(collector); diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index c306446b9780..acd575badbe5 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -48,6 +48,1214 @@ } } }, + "application_usage": { + "properties": { + "dashboards": { + "properties": { + "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" + } + } + }, + "dev_tools": { + "properties": { + "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" + } + } + }, + "discover": { + "properties": { + "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" + } + } + }, + "home": { + "properties": { + "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" + } + } + }, + "kibana": { + "properties": { + "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" + } + } + }, + "management": { + "properties": { + "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" + } + } + }, + "short_url_redirect": { + "properties": { + "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" + } + } + }, + "timelion": { + "properties": { + "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" + } + } + }, + "visualize": { + "properties": { + "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": { + "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" + } + } + }, + "csm": { + "properties": { + "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" + } + } + }, + "canvas": { + "properties": { + "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" + } + } + }, + "dashboard_mode": { + "properties": { + "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" + } + } + }, + "appSearch": { + "properties": { + "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" + } + } + }, + "workplaceSearch": { + "properties": { + "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" + } + } + }, + "graph": { + "properties": { + "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" + } + } + }, + "logs": { + "properties": { + "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" + } + } + }, + "metrics": { + "properties": { + "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" + } + } + }, + "infra": { + "properties": { + "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" + } + } + }, + "ingestManager": { + "properties": { + "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": { + "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" + } + } + }, + "maps": { + "properties": { + "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" + } + } + }, + "ml": { + "properties": { + "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" + } + } + }, + "monitoring": { + "properties": { + "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" + } + } + }, + "observability-overview": { + "properties": { + "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" + } + } + }, + "security_account": { + "properties": { + "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" + } + } + }, + "security_access_agreement": { + "properties": { + "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" + } + } + }, + "security_capture_url": { + "properties": { + "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" + } + } + }, + "security_logged_out": { + "properties": { + "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" + } + } + }, + "security_login": { + "properties": { + "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" + } + } + }, + "security_logout": { + "properties": { + "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" + } + } + }, + "security_overwritten_session": { + "properties": { + "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" + } + } + }, + "securitySolution": { + "properties": { + "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" + } + } + }, + "securitySolution:overview": { + "properties": { + "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" + } + } + }, + "securitySolution:detections": { + "properties": { + "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" + } + } + }, + "securitySolution:hosts": { + "properties": { + "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" + } + } + }, + "securitySolution:network": { + "properties": { + "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" + } + } + }, + "securitySolution:timelines": { + "properties": { + "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" + } + } + }, + "securitySolution:case": { + "properties": { + "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" + } + } + }, + "securitySolution:administration": { + "properties": { + "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" + } + } + }, + "siem": { + "properties": { + "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" + } + } + }, + "space_selector": { + "properties": { + "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" + } + } + }, + "uptime": { + "properties": { + "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" + } + } + } + } + }, "csp": { "properties": { "strict": {