From 1bab449d214bdbdef646379863312f369412215e Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 30 Sep 2021 13:00:41 +0200 Subject: [PATCH 01/51] [Reporting] Add deprecation titles (#113387) (#113510) * added i18n and titles to config deprecations * Updated jest test * Add newline at end of file * remove unneeded space --- .../reporting/server/config/index.test.ts | 4 +- .../plugins/reporting/server/config/index.ts | 42 ++++++++++++++----- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/reporting/server/config/index.test.ts b/x-pack/plugins/reporting/server/config/index.test.ts index 464387ebf90c7..62936fb2f14f3 100644 --- a/x-pack/plugins/reporting/server/config/index.test.ts +++ b/x-pack/plugins/reporting/server/config/index.test.ts @@ -37,7 +37,7 @@ describe('deprecations', () => { const { messages } = applyReportingDeprecations({ index, roles: { enabled: false } }); expect(messages).toMatchInlineSnapshot(` Array [ - "\\"xpack.reporting.index\\" is deprecated. Multitenancy by changing \\"kibana.index\\" will not be supported starting in 8.0. See https://ela.st/kbn-remove-legacy-multitenancy for more details", + "Multitenancy by changing \\"kibana.index\\" will not be supported starting in 8.0. See https://ela.st/kbn-remove-legacy-multitenancy for more details", ] `); }); @@ -47,7 +47,7 @@ describe('deprecations', () => { const { messages } = applyReportingDeprecations({ roles: { enabled: true } }); expect(messages).toMatchInlineSnapshot(` Array [ - "\\"xpack.reporting.roles\\" is deprecated. Granting reporting privilege through a \\"reporting_user\\" role will not be supported starting in 8.0. Please set \\"xpack.reporting.roles.enabled\\" to \\"false\\" and grant reporting privileges to users using Kibana application privileges **Management > Security > Roles**.", + "Granting reporting privilege through a \\"reporting_user\\" role will not be supported starting in 8.0. Please set \\"xpack.reporting.roles.enabled\\" to \\"false\\" and grant reporting privileges to users using Kibana application privileges **Management > Security > Roles**.", ] `); }); diff --git a/x-pack/plugins/reporting/server/config/index.ts b/x-pack/plugins/reporting/server/config/index.ts index 119f49df014e2..c7afdb22f8bdb 100644 --- a/x-pack/plugins/reporting/server/config/index.ts +++ b/x-pack/plugins/reporting/server/config/index.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; import { PluginConfigDescriptor } from 'kibana/server'; import { get } from 'lodash'; import { ConfigSchema, ReportingConfigType } from './schema'; @@ -27,11 +28,21 @@ export const config: PluginConfigDescriptor = { const reporting = get(settings, fromPath); if (reporting?.index) { addDeprecation({ - message: `"${fromPath}.index" is deprecated. Multitenancy by changing "kibana.index" will not be supported starting in 8.0. See https://ela.st/kbn-remove-legacy-multitenancy for more details`, + title: i18n.translate('xpack.reporting.deprecations.reportingIndex.title', { + defaultMessage: 'Setting "{fromPath}.index" is deprecated', + values: { fromPath }, + }), + message: i18n.translate('xpack.reporting.deprecations.reportingIndex.description', { + defaultMessage: `Multitenancy by changing "kibana.index" will not be supported starting in 8.0. See https://ela.st/kbn-remove-legacy-multitenancy for more details`, + }), correctiveActions: { manualSteps: [ - `If you rely on this setting to achieve multitenancy you should use Spaces, cross-cluster replication, or cross-cluster search instead.`, - `To migrate to Spaces, we encourage using saved object management to export your saved objects from a tenant into the default tenant in a space.`, + i18n.translate('xpack.reporting.deprecations.reportingIndex.manualStepOne', { + defaultMessage: `If you rely on this setting to achieve multitenancy you should use Spaces, cross-cluster replication, or cross-cluster search instead.`, + }), + i18n.translate('xpack.reporting.deprecations.reportingIndex.manualStepTwo', { + defaultMessage: `To migrate to Spaces, we encourage using saved object management to export your saved objects from a tenant into the default tenant in a space.`, + }), ], }, }); @@ -39,15 +50,26 @@ export const config: PluginConfigDescriptor = { if (reporting?.roles?.enabled !== false) { addDeprecation({ - message: - `"${fromPath}.roles" is deprecated. Granting reporting privilege through a "reporting_user" role will not be supported ` + - `starting in 8.0. Please set "xpack.reporting.roles.enabled" to "false" and grant reporting privileges to users ` + - `using Kibana application privileges **Management > Security > Roles**.`, + title: i18n.translate('xpack.reporting.deprecations.reportingRoles.title', { + defaultMessage: 'Setting "{fromPath}.roles" is deprecated', + values: { fromPath }, + }), + message: i18n.translate('xpack.reporting.deprecations.reportingRoles.description', { + defaultMessage: + `Granting reporting privilege through a "reporting_user" role will not be supported` + + ` starting in 8.0. Please set "xpack.reporting.roles.enabled" to "false" and grant reporting privileges to users` + + ` using Kibana application privileges **Management > Security > Roles**.`, + }), correctiveActions: { manualSteps: [ - `Set 'xpack.reporting.roles.enabled' to 'false' in your kibana configs.`, - `Grant reporting privileges to users using Kibana application privileges` + - `under **Management > Security > Roles**.`, + i18n.translate('xpack.reporting.deprecations.reportingRoles.manualStepOne', { + defaultMessage: `Set 'xpack.reporting.roles.enabled' to 'false' in your kibana configs.`, + }), + i18n.translate('xpack.reporting.deprecations.reportingRoles.manualStepTwo', { + defaultMessage: + `Grant reporting privileges to users using Kibana application privileges` + + ` under **Management > Security > Roles**.`, + }), ], }, }); From e8c257a8e731a5a84a80db109781208cf07dcb2d Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 07:11:11 -0400 Subject: [PATCH 02/51] [Security Solution][Detections] Fixes 'Detection Rule "reference url" links don't work if they are created with missing "http://' (#112452) (#113509) * updates url validation to don't accept urls without http or https prefix * fixes typo * fixes linter issue * refactors to follow recommendations * Update x-pack/plugins/security_solution/public/common/utils/validators/index.test.ts Co-authored-by: Frank Hassanabad Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Frank Hassanabad Co-authored-by: Gloria Hornero Co-authored-by: Frank Hassanabad --- .../cypress/tasks/alerts_detection_rules.ts | 3 +- .../common/utils/validators/index.test.ts | 30 ++++++++++++++++++- .../public/common/utils/validators/index.ts | 22 +++++++++----- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts index 0e81f75a19046..7d2116cff7bfb 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts @@ -45,6 +45,7 @@ import { RULE_DETAILS_DELETE_BTN, } from '../screens/alerts_detection_rules'; import { ALL_ACTIONS, DELETE_RULE } from '../screens/rule_details'; +import { LOADING_INDICATOR } from '../screens/security_header'; export const activateRule = (rulePosition: number) => { cy.get(RULE_SWITCH).eq(rulePosition).click({ force: true }); @@ -71,7 +72,7 @@ export const duplicateFirstRule = () => { * flake. */ export const duplicateRuleFromMenu = () => { - cy.get(ALL_ACTIONS).should('be.visible'); + cy.get(LOADING_INDICATOR).should('not.exist'); cy.root() .pipe(($el) => { $el.find(ALL_ACTIONS).trigger('click'); diff --git a/x-pack/plugins/security_solution/public/common/utils/validators/index.test.ts b/x-pack/plugins/security_solution/public/common/utils/validators/index.test.ts index 2eebb1723ea1f..2758a9724e897 100644 --- a/x-pack/plugins/security_solution/public/common/utils/validators/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/utils/validators/index.test.ts @@ -9,12 +9,40 @@ import { isUrlInvalid } from '.'; describe('helpers', () => { describe('isUrlInvalid', () => { - test('verifies invalid url', () => { + test('should verify invalid url', () => { expect(isUrlInvalid('this is not a url')).toBeTruthy(); }); + test('should verify as invalid url without http(s):// prefix', () => { + expect(isUrlInvalid('www.thisIsNotValid.com')).toBeTruthy(); + }); + test('verifies valid url', () => { expect(isUrlInvalid('https://www.elastic.co/')).toBeFalsy(); }); + + test('should verify valid wwww such as 4 of them.', () => { + expect(isUrlInvalid('https://wwww.example.com')).toBeFalsy(); + }); + + test('should validate characters such as %22 being part of a correct URL.', () => { + expect(isUrlInvalid('https://www.exam%22ple.com')).toBeFalsy(); + }); + + test('should validate characters incorrectly such as ]', () => { + expect(isUrlInvalid('https://www.example.com[')).toBeTruthy(); + }); + + test('should verify valid http url', () => { + expect(isUrlInvalid('http://www.example.com/')).toBeFalsy(); + }); + + test('should verify as valid when given an empty string', () => { + expect(isUrlInvalid('')).toBeFalsy(); + }); + + test('empty spaces should valid as not valid ', () => { + expect(isUrlInvalid(' ')).toBeTruthy(); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/utils/validators/index.ts b/x-pack/plugins/security_solution/public/common/utils/validators/index.ts index 6d85e1fdd981e..7f0e8c30177f8 100644 --- a/x-pack/plugins/security_solution/public/common/utils/validators/index.ts +++ b/x-pack/plugins/security_solution/public/common/utils/validators/index.ts @@ -5,16 +5,24 @@ * 2.0. */ -import { isEmpty } from 'lodash/fp'; - export * from './is_endpoint_host_isolated'; -const urlExpression = - /(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/gi; +const allowedSchemes = ['http:', 'https:']; export const isUrlInvalid = (url: string | null | undefined) => { - if (!isEmpty(url) && url != null && url.match(urlExpression) == null) { - return true; + try { + if (url != null) { + if (url === '') { + return false; + } else { + const urlParsed = new URL(url); + if (allowedSchemes.includes(urlParsed.protocol)) { + return false; + } + } + } + } catch (error) { + // intentionally left empty } - return false; + return true; }; From 04c90e8fabf6defbf857b2e30987118f316abcf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loix?= Date: Thu, 30 Sep 2021 12:45:59 +0100 Subject: [PATCH 03/51] [7.x] [Mappings editor] Remove boost parameter from field types (#113142) (#113512) * [Mappings editor] Remove boost parameter from field types (#113142) # Conflicts: # x-pack/plugins/index_management/__jest__/client_integration/helpers/constants.ts # x-pack/plugins/index_management/public/application/lib/indices.ts # x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts * Change MAJOR_VERSION to 7.16.0 --- .../forms/hook_form_lib/hooks/use_field.ts | 27 +- .../client_integration/helpers/constants.ts | 8 - .../helpers/setup_environment.tsx | 35 ++- .../common/constants/index.ts | 2 + .../common/constants/plugin.ts | 6 + .../plugins/index_management/common/index.ts | 2 +- .../public/application/app_context.tsx | 2 + .../datatypes/scaled_float_datatype.test.tsx | 9 +- .../datatypes/text_datatype.test.tsx | 10 +- .../client_integration/helpers/index.ts | 1 + .../helpers/mappings_editor.helpers.tsx | 8 +- .../helpers/setup_environment.tsx | 8 + .../field_parameters/boost_parameter.tsx | 1 + .../fields/edit_field/edit_field.tsx | 291 +++++++++--------- .../edit_field/edit_field_container.tsx | 5 + .../fields/field_types/boolean_type.tsx | 9 +- .../fields/field_types/date_type.tsx | 10 +- .../fields/field_types/flattened_type.tsx | 9 +- .../fields/field_types/index.ts | 3 + .../fields/field_types/ip_type.tsx | 9 +- .../fields/field_types/keyword_type.tsx | 10 +- .../fields/field_types/numeric_type.tsx | 9 +- .../fields/field_types/range_type.tsx | 9 +- .../fields/field_types/text_type.tsx | 9 +- .../fields/field_types/token_count_type.tsx | 10 +- .../public/application/index.tsx | 35 ++- .../public/application/lib/indices.ts | 7 +- .../application/mount_management_section.ts | 5 +- .../store/selectors/indices_filter.test.ts | 7 +- .../plugins/index_management/public/index.ts | 5 +- .../plugins/index_management/public/plugin.ts | 9 +- .../index_management/public/shared_imports.ts | 1 + 32 files changed, 348 insertions(+), 223 deletions(-) delete mode 100644 x-pack/plugins/index_management/__jest__/client_integration/helpers/constants.ts diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts index af6904dbacdd9..dedc390c47719 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts @@ -504,18 +504,21 @@ export const useField = ( const { resetValue = true, defaultValue: updatedDefaultValue } = resetOptions; setPristine(true); - setIsModified(false); - setValidating(false); - setIsChangingValue(false); - setIsValidated(false); - setStateErrors([]); - - if (resetValue) { - hasBeenReset.current = true; - const newValue = deserializeValue(updatedDefaultValue ?? defaultValue); - // updateStateIfMounted('value', newValue); - setValue(newValue); - return newValue; + + if (isMounted.current) { + setIsModified(false); + setValidating(false); + setIsChangingValue(false); + setIsValidated(false); + setStateErrors([]); + + if (resetValue) { + hasBeenReset.current = true; + const newValue = deserializeValue(updatedDefaultValue ?? defaultValue); + // updateStateIfMounted('value', newValue); + setValue(newValue); + return newValue; + } } }, [deserializeValue, defaultValue, setValue, setStateErrors] diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/constants.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/constants.ts deleted file mode 100644 index e241dfebd08ee..0000000000000 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/constants.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export const MAJOR_VERSION = '7.16.0'; diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx index 3066742d4c0e0..9f8199215df5b 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx @@ -14,9 +14,12 @@ import { SemVer } from 'semver'; import { notificationServiceMock, docLinksServiceMock, + uiSettingsServiceMock, } from '../../../../../../src/core/public/mocks'; import { GlobalFlyout } from '../../../../../../src/plugins/es_ui_shared/public'; +import { createKibanaReactContext } from '../../../../../../src/plugins/kibana_react/public'; +import { MAJOR_VERSION } from '../../../common'; import { AppContextProvider } from '../../../public/application/app_context'; import { httpService } from '../../../public/application/services/http'; import { breadcrumbService } from '../../../public/application/services/breadcrumbs'; @@ -32,13 +35,10 @@ import { } from '../../../public/application/components'; import { componentTemplatesMockDependencies } from '../../../public/application/components/component_templates/__jest__'; import { init as initHttpRequests } from './http_requests'; -import { MAJOR_VERSION } from './constants'; const mockHttpClient = axios.create({ adapter: axiosXhrAdapter }); const { GlobalFlyoutProvider } = GlobalFlyout; -export const kibanaVersion = new SemVer(MAJOR_VERSION); - export const services = { extensionsService: new ExtensionsService(), uiMetricService: new UiMetricService('index_management'), @@ -54,6 +54,15 @@ const appDependencies = { plugins: {}, } as any; +export const kibanaVersion = new SemVer(MAJOR_VERSION); + +const { Provider: KibanaReactContextProvider } = createKibanaReactContext({ + uiSettings: uiSettingsServiceMock.createSetupContract(), + kibanaVersion: { + get: () => kibanaVersion, + }, +}); + export const setupEnvironment = () => { // Mock initialization of services // @ts-ignore @@ -75,14 +84,16 @@ export const WithAppDependencies = (props: any) => { const mergedDependencies = merge({}, appDependencies, overridingDependencies); return ( - - - - - - - - - + + + + + + + + + + + ); }; diff --git a/x-pack/plugins/index_management/common/constants/index.ts b/x-pack/plugins/index_management/common/constants/index.ts index a2ad51e5c89f0..373044aef9d45 100644 --- a/x-pack/plugins/index_management/common/constants/index.ts +++ b/x-pack/plugins/index_management/common/constants/index.ts @@ -53,3 +53,5 @@ export { UIM_TEMPLATE_CLONE, UIM_TEMPLATE_SIMULATE, } from './ui_metric'; + +export { MAJOR_VERSION } from './plugin'; diff --git a/x-pack/plugins/index_management/common/constants/plugin.ts b/x-pack/plugins/index_management/common/constants/plugin.ts index 605dbf3859a0e..d52b3cf28bb4a 100644 --- a/x-pack/plugins/index_management/common/constants/plugin.ts +++ b/x-pack/plugins/index_management/common/constants/plugin.ts @@ -17,3 +17,9 @@ export const PLUGIN = { defaultMessage: 'Index Management', }), }; + +// Ideally we want to access the Kibana major version from core +// "PluginInitializerContext.env.packageInfo.version". In some cases it is not possible +// to dynamically inject that version without a huge refactor on the code base. +// We will then keep this single constant to declare on which major branch we are. +export const MAJOR_VERSION = '7.16.0'; diff --git a/x-pack/plugins/index_management/common/index.ts b/x-pack/plugins/index_management/common/index.ts index ed8fd87643946..127123609b186 100644 --- a/x-pack/plugins/index_management/common/index.ts +++ b/x-pack/plugins/index_management/common/index.ts @@ -8,7 +8,7 @@ // TODO: https://github.com/elastic/kibana/issues/110892 /* eslint-disable @kbn/eslint/no_export_all */ -export { API_BASE_PATH, BASE_PATH } from './constants'; +export { API_BASE_PATH, BASE_PATH, MAJOR_VERSION } from './constants'; export { getTemplateParameter } from './lib'; diff --git a/x-pack/plugins/index_management/public/application/app_context.tsx b/x-pack/plugins/index_management/public/application/app_context.tsx index f8ebfdf7c46b7..f562ab9d15a8b 100644 --- a/x-pack/plugins/index_management/public/application/app_context.tsx +++ b/x-pack/plugins/index_management/public/application/app_context.tsx @@ -7,6 +7,7 @@ import React, { createContext, useContext } from 'react'; import { ScopedHistory } from 'kibana/public'; +import { SemVer } from 'semver'; import { ManagementAppMountParams } from 'src/plugins/management/public'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; @@ -37,6 +38,7 @@ export interface AppDependencies { uiSettings: CoreSetup['uiSettings']; url: SharePluginStart['url']; docLinks: CoreStart['docLinks']; + kibanaVersion: SemVer; } export const AppContextProvider = ({ diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/scaled_float_datatype.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/scaled_float_datatype.test.tsx index b6af657ee5f96..17e7317e098cc 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/scaled_float_datatype.test.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/scaled_float_datatype.test.tsx @@ -7,7 +7,7 @@ import { act } from 'react-dom/test-utils'; -import { componentHelpers, MappingsEditorTestBed } from '../helpers'; +import { componentHelpers, MappingsEditorTestBed, kibanaVersion } from '../helpers'; const { setup, getMappingsEditorDataFactory } = componentHelpers.mappingsEditor; @@ -92,6 +92,13 @@ describe('Mappings editor: scaled float datatype', () => { await updateFieldAndCloseFlyout(); expect(exists('mappingsEditorFieldEdit')).toBe(false); + if (kibanaVersion.major < 7) { + expect(exists('boostParameterToggle')).toBe(true); + } else { + // Since 8.x the boost parameter is deprecated + expect(exists('boostParameterToggle')).toBe(false); + } + // It should have the default parameters values added, plus the scaling factor updatedMappings.properties.myField = { ...defaultScaledFloatParameters, diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/text_datatype.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/text_datatype.test.tsx index ebca896de0b86..db8678478aa3d 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/text_datatype.test.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/text_datatype.test.tsx @@ -7,7 +7,7 @@ import { act } from 'react-dom/test-utils'; -import { componentHelpers, MappingsEditorTestBed } from '../helpers'; +import { componentHelpers, MappingsEditorTestBed, kibanaVersion } from '../helpers'; import { getFieldConfig } from '../../../lib'; const { setup, getMappingsEditorDataFactory } = componentHelpers.mappingsEditor; @@ -64,6 +64,7 @@ describe('Mappings editor: text datatype', () => { const { component, + exists, actions: { startEditField, getToggleValue, updateFieldAndCloseFlyout }, } = testBed; @@ -74,6 +75,13 @@ describe('Mappings editor: text datatype', () => { const indexFieldConfig = getFieldConfig('index'); expect(getToggleValue('indexParameter.formRowToggle')).toBe(indexFieldConfig.defaultValue); + if (kibanaVersion.major < 7) { + expect(exists('boostParameterToggle')).toBe(true); + } else { + // Since 8.x the boost parameter is deprecated + expect(exists('boostParameterToggle')).toBe(false); + } + // Save the field and close the flyout await updateFieldAndCloseFlyout(); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts index bec4e5a5c88a1..fbe24557ae6a1 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts @@ -13,6 +13,7 @@ import { } from './mappings_editor.helpers'; export { nextTick, getRandomString, findTestSubject, TestBed } from '@kbn/test/jest'; +export { kibanaVersion } from './setup_environment'; export const componentHelpers = { mappingsEditor: { setup: mappingsEditorSetup, getMappingsEditorDataFactory }, diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx index 3110b3ce24041..074d96e9be4c1 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx @@ -123,12 +123,10 @@ const createActions = (testBed: TestBed) => { component.update(); - if (subType !== undefined) { + if (subType !== undefined && type === 'other') { await act(async () => { - if (type === 'other') { - // subType is a text input - form.setInputValue('createFieldForm.fieldSubType', subType); - } + // subType is a text input + form.setInputValue('createFieldForm.fieldSubType', subType); }); } diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/setup_environment.tsx index 7055dcc74ce7b..5e3ae3c1544ae 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/setup_environment.tsx @@ -6,6 +6,8 @@ */ import React from 'react'; +import { SemVer } from 'semver'; + /* eslint-disable-next-line @kbn/eslint/no-restricted-paths */ import '../../../../../../../../../../src/plugins/es_ui_shared/public/components/code_editor/jest_mock'; import { GlobalFlyout } from '../../../../../../../../../../src/plugins/es_ui_shared/public'; @@ -13,9 +15,12 @@ import { docLinksServiceMock, uiSettingsServiceMock, } from '../../../../../../../../../../src/core/public/mocks'; +import { MAJOR_VERSION } from '../../../../../../../common'; import { MappingsEditorProvider } from '../../../mappings_editor_context'; import { createKibanaReactContext } from '../../../shared_imports'; +export const kibanaVersion = new SemVer(MAJOR_VERSION); + jest.mock('@elastic/eui', () => { const original = jest.requireActual('@elastic/eui'); @@ -72,6 +77,9 @@ const { GlobalFlyoutProvider } = GlobalFlyout; const { Provider: KibanaReactContextProvider } = createKibanaReactContext({ uiSettings: uiSettingsServiceMock.createSetupContract(), + kibanaVersion: { + get: () => kibanaVersion, + }, }); const defaultProps = { diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx index edafb57ad2e57..07156611461d6 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx @@ -33,6 +33,7 @@ export const BoostParameter = ({ defaultToggleValue }: Props) => ( href: documentationService.getBoostLink(), }} defaultToggleValue={defaultToggleValue} + data-test-subj="boostParameter" > {/* Boost level */} , which wrapps the form with @@ -50,161 +52,164 @@ export interface Props { // the height calculaction and does not render the footer position correctly. const FormWrapper: React.FC = ({ children }) => <>{children}; -export const EditField = React.memo(({ form, field, allFields, exitEdit, updateField }: Props) => { - const submitForm = async () => { - const { isValid, data } = await form.submit(); - - if (isValid) { - updateField({ ...field, source: data }); - } - }; - - const { isMultiField } = field; - - return ( -
- - - - {/* We need an extra div to get out of flex grow */} -
- {/* Title */} - -

- {isMultiField - ? i18n.translate('xpack.idxMgmt.mappingsEditor.editMultiFieldTitle', { - defaultMessage: "Edit multi-field '{fieldName}'", - values: { - fieldName: limitStringLength(field.source.name), - }, - }) - : i18n.translate('xpack.idxMgmt.mappingsEditor.editFieldTitle', { - defaultMessage: "Edit field '{fieldName}'", +export const EditField = React.memo( + ({ form, field, allFields, exitEdit, updateField, kibanaVersion }: Props) => { + const submitForm = async () => { + const { isValid, data } = await form.submit(); + + if (isValid) { + updateField({ ...field, source: data }); + } + }; + + const { isMultiField } = field; + + return ( + + + + + {/* We need an extra div to get out of flex grow */} +
+ {/* Title */} + +

+ {isMultiField + ? i18n.translate('xpack.idxMgmt.mappingsEditor.editMultiFieldTitle', { + defaultMessage: "Edit multi-field '{fieldName}'", + values: { + fieldName: limitStringLength(field.source.name), + }, + }) + : i18n.translate('xpack.idxMgmt.mappingsEditor.editFieldTitle', { + defaultMessage: "Edit field '{fieldName}'", + values: { + fieldName: limitStringLength(field.source.name), + }, + })} +

+
+
+
+ + {/* Documentation link */} + + {({ type, subType }) => { + const linkDocumentation = + documentationService.getTypeDocLink(subType?.[0]?.value) || + documentationService.getTypeDocLink(type?.[0]?.value); + + if (!linkDocumentation) { + return null; + } + + const typeDefinition = TYPE_DEFINITION[type[0].value as MainType]; + const subTypeDefinition = TYPE_DEFINITION[subType?.[0].value as SubType]; + + return ( + + + {i18n.translate('xpack.idxMgmt.mappingsEditor.editField.typeDocumentation', { + defaultMessage: '{type} documentation', values: { - fieldName: limitStringLength(field.source.name), + type: subTypeDefinition ? subTypeDefinition.label : typeDefinition.label, }, })} -

-
-
-
+ + + ); + }} + +
+ + {/* Field path */} + + + {field.path.join(' > ')} + + +
+ + + - {/* Documentation link */} {({ type, subType }) => { - const linkDocumentation = - documentationService.getTypeDocLink(subType?.[0]?.value) || - documentationService.getTypeDocLink(type?.[0]?.value); + const ParametersForm = getParametersFormForType(type?.[0].value, subType?.[0].value); - if (!linkDocumentation) { + if (!ParametersForm) { return null; } - const typeDefinition = TYPE_DEFINITION[type[0].value as MainType]; - const subTypeDefinition = TYPE_DEFINITION[subType?.[0].value as SubType]; - return ( - - - {i18n.translate('xpack.idxMgmt.mappingsEditor.editField.typeDocumentation', { - defaultMessage: '{type} documentation', - values: { - type: subTypeDefinition ? subTypeDefinition.label : typeDefinition.label, - }, - })} - - + ); }} - - - {/* Field path */} - - - {field.path.join(' > ')} - - - - - - - - - {({ type, subType }) => { - const ParametersForm = getParametersFormForType(type?.[0].value, subType?.[0].value); - - if (!ParametersForm) { - return null; - } - - return ( - + + + {form.isSubmitted && !form.isValid && ( + <> + - ); - }} - - - - - {form.isSubmitted && !form.isValid && ( - <> - - - - )} - - - - - {i18n.translate('xpack.idxMgmt.mappingsEditor.editFieldCancelButtonLabel', { - defaultMessage: 'Cancel', - })} - - - - - {i18n.translate('xpack.idxMgmt.mappingsEditor.editFieldUpdateButtonLabel', { - defaultMessage: 'Update', - })} - - - - - - ); -}); + + + )} + + + + + {i18n.translate('xpack.idxMgmt.mappingsEditor.editFieldCancelButtonLabel', { + defaultMessage: 'Cancel', + })} + + + + + {i18n.translate('xpack.idxMgmt.mappingsEditor.editFieldUpdateButtonLabel', { + defaultMessage: 'Update', + })} + + + + + + ); + } +); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx index 9a9e1179db237..2db0d2b6c544b 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx @@ -7,6 +7,7 @@ import React, { useEffect, useMemo } from 'react'; +import { useKibana } from '../../../../../../index'; import { useForm } from '../../../../shared_imports'; import { useDispatch, useMappingsState } from '../../../../mappings_state_context'; import { Field } from '../../../../types'; @@ -30,6 +31,9 @@ export const EditFieldContainer = React.memo(({ exitEdit }: Props) => { const { fields, documentFields } = useMappingsState(); const dispatch = useDispatch(); const { updateField, modal } = useUpdateField(); + const { + services: { kibanaVersion }, + } = useKibana(); const { status, fieldToEdit } = documentFields; const isEditing = status === 'editingField'; @@ -73,6 +77,7 @@ export const EditFieldContainer = React.memo(({ exitEdit }: Props) => { allFields={fields.byId} exitEdit={exitEdit} updateField={updateField} + kibanaVersion={kibanaVersion.get()} /> {renderModal()} diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx index 2cba31279b270..ad7f7e6d93c41 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { SemVer } from 'semver'; import { i18n } from '@kbn/i18n'; @@ -57,9 +58,10 @@ const nullValueOptions = [ interface Props { field: NormalizedField; + kibanaVersion: SemVer; } -export const BooleanType = ({ field }: Props) => { +export const BooleanType = ({ field, kibanaVersion }: Props) => { return ( <> @@ -96,7 +98,10 @@ export const BooleanType = ({ field }: Props) => { - + {/* The "boost" parameter is deprecated since 8.x */} + {kibanaVersion.major < 8 && ( + + )} ); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx index eed3c48804266..ea71e7fcce5d2 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; - +import { SemVer } from 'semver'; import { i18n } from '@kbn/i18n'; import { NormalizedField, Field as FieldType } from '../../../../types'; @@ -43,9 +43,10 @@ const getDefaultToggleValue = (param: string, field: FieldType) => { interface Props { field: NormalizedField; + kibanaVersion: SemVer; } -export const DateType = ({ field }: Props) => { +export const DateType = ({ field, kibanaVersion }: Props) => { return ( <> @@ -79,7 +80,10 @@ export const DateType = ({ field }: Props) => { - + {/* The "boost" parameter is deprecated since 8.x */} + {kibanaVersion.major < 8 && ( + + )} ); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx index 6759f66e0db38..0f58c75ca9cb7 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; +import { SemVer } from 'semver'; import { NormalizedField, Field as FieldType } from '../../../../types'; import { UseField, Field } from '../../../../shared_imports'; @@ -27,6 +28,7 @@ import { BasicParametersSection, EditFieldFormRow, AdvancedParametersSection } f interface Props { field: NormalizedField; + kibanaVersion: SemVer; } const getDefaultToggleValue = (param: string, field: FieldType) => { @@ -45,7 +47,7 @@ const getDefaultToggleValue = (param: string, field: FieldType) => { } }; -export const FlattenedType = React.memo(({ field }: Props) => { +export const FlattenedType = React.memo(({ field, kibanaVersion }: Props) => { return ( <> @@ -89,7 +91,10 @@ export const FlattenedType = React.memo(({ field }: Props) => { - + {/* The "boost" parameter is deprecated since 8.x */} + {kibanaVersion.major < 8 && ( + + )} ); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts index c7aab6fbe09ae..f62a19e55a835 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts @@ -6,6 +6,8 @@ */ import { ComponentType } from 'react'; +import { SemVer } from 'semver'; + import { MainType, SubType, DataType, NormalizedField, NormalizedFields } from '../../../../types'; import { AliasType } from './alias_type'; @@ -75,6 +77,7 @@ export const getParametersFormForType = ( field: NormalizedField; allFields: NormalizedFields['byId']; isMultiField: boolean; + kibanaVersion: SemVer; }> | undefined => subType === undefined diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx index d3f2ae5c6bf0e..3ea56805829d5 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { SemVer } from 'semver'; import { NormalizedField, Field as FieldType } from '../../../../types'; import { getFieldConfig } from '../../../../lib'; @@ -36,9 +37,10 @@ const getDefaultToggleValue = (param: string, field: FieldType) => { interface Props { field: NormalizedField; + kibanaVersion: SemVer; } -export const IpType = ({ field }: Props) => { +export const IpType = ({ field, kibanaVersion }: Props) => { return ( <> @@ -54,7 +56,10 @@ export const IpType = ({ field }: Props) => { - + {/* The "boost" parameter is deprecated since 8.x */} + {kibanaVersion.major < 8 && ( + + )} ); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx index 30329925f844f..9d820c1b07636 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; - +import { SemVer } from 'semver'; import { i18n } from '@kbn/i18n'; import { documentationService } from '../../../../../../services/documentation'; @@ -48,9 +48,10 @@ const getDefaultToggleValue = (param: string, field: FieldType) => { interface Props { field: NormalizedField; + kibanaVersion: SemVer; } -export const KeywordType = ({ field }: Props) => { +export const KeywordType = ({ field, kibanaVersion }: Props) => { return ( <> @@ -104,7 +105,10 @@ export const KeywordType = ({ field }: Props) => { - + {/* The "boost" parameter is deprecated since 8.x */} + {kibanaVersion.major < 8 && ( + + )} ); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx index 9d9778b14b954..7035a730f15f4 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; +import { SemVer } from 'semver'; import { NormalizedField, Field as FieldType, ComboBoxOption } from '../../../../types'; import { getFieldConfig } from '../../../../lib'; @@ -43,9 +44,10 @@ const getDefaultToggleValue = (param: string, field: FieldType) => { interface Props { field: NormalizedField; + kibanaVersion: SemVer; } -export const NumericType = ({ field }: Props) => { +export const NumericType = ({ field, kibanaVersion }: Props) => { return ( <> @@ -102,7 +104,10 @@ export const NumericType = ({ field }: Props) => { - + {/* The "boost" parameter is deprecated since 8.x */} + {kibanaVersion.major < 8 && ( + + )} ); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx index ccfa049d9e777..9b8dae490d819 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { SemVer } from 'semver'; import { NormalizedField, @@ -32,9 +33,10 @@ const getDefaultToggleValue = (param: ParameterName, field: FieldType) => { interface Props { field: NormalizedField; + kibanaVersion: SemVer; } -export const RangeType = ({ field }: Props) => { +export const RangeType = ({ field, kibanaVersion }: Props) => { return ( <> @@ -67,7 +69,10 @@ export const RangeType = ({ field }: Props) => { - + {/* The "boost" parameter is deprecated since 8.x */} + {kibanaVersion.major < 8 && ( + + )} ); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx index 2a007e7741d4a..6857e20dc1ec4 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { EuiSpacer, EuiDualRange, EuiFormRow, EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { SemVer } from 'semver'; import { documentationService } from '../../../../../../services/documentation'; import { NormalizedField, Field as FieldType } from '../../../../types'; @@ -36,6 +37,7 @@ import { BasicParametersSection, EditFieldFormRow, AdvancedParametersSection } f interface Props { field: NormalizedField; + kibanaVersion: SemVer; } const getDefaultToggleValue = (param: string, field: FieldType) => { @@ -73,7 +75,7 @@ const getDefaultToggleValue = (param: string, field: FieldType) => { } }; -export const TextType = React.memo(({ field }: Props) => { +export const TextType = React.memo(({ field, kibanaVersion }: Props) => { const onIndexPrefixesChanage = (minField: FieldHook, maxField: FieldHook) => ([min, max]: any) => { @@ -246,7 +248,10 @@ export const TextType = React.memo(({ field }: Props) => { - + {/* The "boost" parameter is deprecated since 8.x */} + {kibanaVersion.major < 8 && ( + + )} ); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx index 2c56100ede037..3c0e8a28f3090 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; - +import { SemVer } from 'semver'; import { i18n } from '@kbn/i18n'; import { documentationService } from '../../../../../../services/documentation'; @@ -43,9 +43,10 @@ const getDefaultToggleValue = (param: string, field: FieldType) => { interface Props { field: NormalizedField; + kibanaVersion: SemVer; } -export const TokenCountType = ({ field }: Props) => { +export const TokenCountType = ({ field, kibanaVersion }: Props) => { return ( <> @@ -113,7 +114,10 @@ export const TokenCountType = ({ field }: Props) => { - + {/* The "boost" parameter is deprecated since 8.x */} + {kibanaVersion.major < 8 && ( + + )} ); diff --git a/x-pack/plugins/index_management/public/application/index.tsx b/x-pack/plugins/index_management/public/application/index.tsx index 758470604fc5a..fc64dad0ae7ba 100644 --- a/x-pack/plugins/index_management/public/application/index.tsx +++ b/x-pack/plugins/index_management/public/application/index.tsx @@ -8,11 +8,16 @@ import React from 'react'; import { Provider } from 'react-redux'; import { render, unmountComponentAtNode } from 'react-dom'; +import { SemVer } from 'semver'; -import { CoreStart } from '../../../../../src/core/public'; +import { CoreStart, CoreSetup } from '../../../../../src/core/public'; import { API_BASE_PATH } from '../../common'; -import { createKibanaReactContext, GlobalFlyout } from '../shared_imports'; +import { + createKibanaReactContext, + GlobalFlyout, + useKibana as useKibanaReactPlugin, +} from '../shared_imports'; import { AppContextProvider, AppDependencies } from './app_context'; import { App } from './app'; @@ -31,12 +36,16 @@ export const renderApp = ( const { i18n, docLinks, notifications, application } = core; const { Context: I18nContext } = i18n; - const { services, history, setBreadcrumbs, uiSettings } = dependencies; + const { services, history, setBreadcrumbs, uiSettings, kibanaVersion } = dependencies; // uiSettings is required by the CodeEditor component used to edit runtime field Painless scripts. - const { Provider: KibanaReactContextProvider } = createKibanaReactContext({ - uiSettings, - }); + const { Provider: KibanaReactContextProvider } = + createKibanaReactContext({ + uiSettings, + kibanaVersion: { + get: () => kibanaVersion, + }, + }); const componentTemplateProviderValues = { httpClient: services.httpService.httpClient, @@ -72,4 +81,16 @@ export const renderApp = ( }; }; -export { AppDependencies }; +interface KibanaReactContextServices { + uiSettings: CoreSetup['uiSettings']; + kibanaVersion: { + get: () => SemVer; + }; +} + +// We override useKibana() from the react plugin to return a typed version for this app +const useKibana = () => { + return useKibanaReactPlugin(); +}; + +export { AppDependencies, useKibana }; diff --git a/x-pack/plugins/index_management/public/application/lib/indices.ts b/x-pack/plugins/index_management/public/application/lib/indices.ts index 71c3cc8e2a3c3..fc93aa6f54448 100644 --- a/x-pack/plugins/index_management/public/application/lib/indices.ts +++ b/x-pack/plugins/index_management/public/application/lib/indices.ts @@ -4,11 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import SemVer from 'semver/classes/semver'; +import { SemVer } from 'semver'; + +import { MAJOR_VERSION } from '../../../common'; import { Index } from '../../../common'; -const version = '7.16.0'; -const kibanaVersion = new SemVer(version); +const kibanaVersion = new SemVer(MAJOR_VERSION); export const isHiddenIndex = (index: Index): boolean => { if (kibanaVersion.major < 8) { diff --git a/x-pack/plugins/index_management/public/application/mount_management_section.ts b/x-pack/plugins/index_management/public/application/mount_management_section.ts index 083a8831291dd..17ac095128a76 100644 --- a/x-pack/plugins/index_management/public/application/mount_management_section.ts +++ b/x-pack/plugins/index_management/public/application/mount_management_section.ts @@ -6,6 +6,7 @@ */ import { i18n } from '@kbn/i18n'; +import { SemVer } from 'semver'; import { CoreSetup } from 'src/core/public'; import { ManagementAppMountParams } from 'src/plugins/management/public/'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; @@ -50,7 +51,8 @@ export async function mountManagementSection( usageCollection: UsageCollectionSetup, params: ManagementAppMountParams, extensionsService: ExtensionsService, - isFleetEnabled: boolean + isFleetEnabled: boolean, + kibanaVersion: SemVer ) { const { element, setBreadcrumbs, history } = params; const [core, startDependencies] = await coreSetup.getStartServices(); @@ -88,6 +90,7 @@ export async function mountManagementSection( uiSettings, url, docLinks, + kibanaVersion, }; const unmountAppCallback = renderApp(element, { core, dependencies: appDependencies }); diff --git a/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts b/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts index 3e62878706a28..b32f2736a9684 100644 --- a/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts +++ b/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts @@ -4,15 +4,16 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import SemVer from 'semver/classes/semver'; +import { SemVer } from 'semver'; + +import { MAJOR_VERSION } from '../../../../common'; import { ExtensionsService } from '../../../services'; import { getFilteredIndices } from '.'; // @ts-ignore import { defaultTableState } from '../reducers/table_state'; import { setExtensionsService } from './extension_service'; -const version = '7.16.0'; -const kibanaVersion = new SemVer(version); +const kibanaVersion = new SemVer(MAJOR_VERSION); describe('getFilteredIndices selector', () => { let extensionService: ExtensionsService; diff --git a/x-pack/plugins/index_management/public/index.ts b/x-pack/plugins/index_management/public/index.ts index 10feabf0a9d0f..f9ccc788a36c0 100644 --- a/x-pack/plugins/index_management/public/index.ts +++ b/x-pack/plugins/index_management/public/index.ts @@ -6,11 +6,12 @@ */ import './index.scss'; +import { PluginInitializerContext } from 'src/core/public'; import { IndexMgmtUIPlugin } from './plugin'; /** @public */ -export const plugin = () => { - return new IndexMgmtUIPlugin(); +export const plugin = (ctx: PluginInitializerContext) => { + return new IndexMgmtUIPlugin(ctx); }; export { IndexManagementPluginSetup } from './types'; diff --git a/x-pack/plugins/index_management/public/plugin.ts b/x-pack/plugins/index_management/public/plugin.ts index 44854a9de9a0f..b7e810b15dbf9 100644 --- a/x-pack/plugins/index_management/public/plugin.ts +++ b/x-pack/plugins/index_management/public/plugin.ts @@ -6,8 +6,9 @@ */ import { i18n } from '@kbn/i18n'; +import { SemVer } from 'semver'; -import { CoreSetup } from '../../../../src/core/public'; +import { CoreSetup, PluginInitializerContext } from '../../../../src/core/public'; import { setExtensionsService } from './application/store/selectors/extension_service'; import { ExtensionsService } from './services'; @@ -20,7 +21,7 @@ import { PLUGIN } from '../common/constants/plugin'; export class IndexMgmtUIPlugin { private extensionsService = new ExtensionsService(); - constructor() { + constructor(private ctx: PluginInitializerContext) { // Temporary hack to provide the service instances in module files in order to avoid a big refactor // For the selectors we should expose them through app dependencies and read them from there on each container component. setExtensionsService(this.extensionsService); @@ -31,6 +32,7 @@ export class IndexMgmtUIPlugin { plugins: SetupDependencies ): IndexManagementPluginSetup { const { fleet, usageCollection, management } = plugins; + const kibanaVersion = new SemVer(this.ctx.env.packageInfo.version); management.sections.section.data.registerApp({ id: PLUGIN.id, @@ -43,7 +45,8 @@ export class IndexMgmtUIPlugin { usageCollection, params, this.extensionsService, - Boolean(fleet) + Boolean(fleet), + kibanaVersion ); }, }); diff --git a/x-pack/plugins/index_management/public/shared_imports.ts b/x-pack/plugins/index_management/public/shared_imports.ts index 275f8af818caf..4e1c420795904 100644 --- a/x-pack/plugins/index_management/public/shared_imports.ts +++ b/x-pack/plugins/index_management/public/shared_imports.ts @@ -56,4 +56,5 @@ export { isJSON } from '../../../../src/plugins/es_ui_shared/static/validators/s export { createKibanaReactContext, reactRouterNavigate, + useKibana, } from '../../../../src/plugins/kibana_react/public'; From 7b4f882a30e399bc49e123994fc315d57f2899b0 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 30 Sep 2021 14:46:38 +0300 Subject: [PATCH 04/51] Revert [Vega] Fix: Scrollbars are appearing in default Vega configurations (#113110) (#113513) * Revert [Vega] Fix: Scrollbars are appearing in default Vega configurations Revert: #97210 * update screens Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/__snapshots__/vega_visualization.test.js.snap | 6 +++--- src/plugins/vis_types/vega/public/components/vega_vis.scss | 2 +- .../vis_types/vega/public/vega_view/vega_base_view.js | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.js.snap b/src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.js.snap index 8915dbcc149c4..c70c4406a34f2 100644 --- a/src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.js.snap +++ b/src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`VegaVisualizations VegaVisualization - basics should show vega graph (may fail in dev env) 1`] = `"
"`; +exports[`VegaVisualizations VegaVisualization - basics should show vega graph (may fail in dev env) 1`] = `"
"`; -exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 1`] = `"
  • \\"width\\" and \\"height\\" params are ignored because \\"autosize\\" is enabled. Set \\"autosize\\": \\"none\\" to disable
"`; +exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 1`] = `"
  • \\"width\\" and \\"height\\" params are ignored because \\"autosize\\" is enabled. Set \\"autosize\\": \\"none\\" to disable
"`; -exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 2`] = `"
  • \\"width\\" and \\"height\\" params are ignored because \\"autosize\\" is enabled. Set \\"autosize\\": \\"none\\" to disable
"`; +exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 2`] = `"
  • \\"width\\" and \\"height\\" params are ignored because \\"autosize\\" is enabled. Set \\"autosize\\": \\"none\\" to disable
"`; diff --git a/src/plugins/vis_types/vega/public/components/vega_vis.scss b/src/plugins/vis_types/vega/public/components/vega_vis.scss index 5b96eb9a560c7..f0062869e0046 100644 --- a/src/plugins/vis_types/vega/public/components/vega_vis.scss +++ b/src/plugins/vis_types/vega/public/components/vega_vis.scss @@ -18,7 +18,7 @@ z-index: 0; flex: 1 1 100%; - //display determined by js + display: block; max-width: 100%; max-height: 100%; width: 100%; diff --git a/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js b/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js index a41197293bbdc..741586b983a71 100644 --- a/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js +++ b/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js @@ -83,10 +83,9 @@ export class VegaBaseView { return; } - const containerDisplay = this._parser.useResize ? 'flex' : 'block'; this._$container = $('
') // Force a height here because css is not loaded in mocha test - .css({ height: '100%', display: containerDisplay }) + .css('height', '100%') .appendTo(this._$parentEl); this._$controls = $( `
` From 1438f4dbe755b76dd54e35f272ed2316d67d7344 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 07:57:08 -0400 Subject: [PATCH 05/51] [Exploratory View] Add to case button (#112463) (#113515) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Shahzad --- .../observability/public/application/types.ts | 2 + .../header/add_to_case_action.test.tsx | 54 ++++++++ .../header/add_to_case_action.tsx | 89 ++++++++++++ .../shared/exploratory_view/header/header.tsx | 4 + .../hooks/use_add_to_case.test.tsx | 68 ++++++++++ .../exploratory_view/hooks/use_add_to_case.ts | 128 ++++++++++++++++++ .../shared/exploratory_view/rtl_helpers.tsx | 2 + 7 files changed, 347 insertions(+) create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_add_to_case.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_add_to_case.ts diff --git a/x-pack/plugins/observability/public/application/types.ts b/x-pack/plugins/observability/public/application/types.ts index 09c5de1e694c8..0bf3fd1ea5a03 100644 --- a/x-pack/plugins/observability/public/application/types.ts +++ b/x-pack/plugins/observability/public/application/types.ts @@ -20,6 +20,7 @@ import { IStorageWrapper } from '../../../../../src/plugins/kibana_utils/public' import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; import { LensPublicStart } from '../../../lens/public'; import { TriggersAndActionsUIPublicPluginStart } from '../../../triggers_actions_ui/public'; +import { CasesUiStart } from '../../../cases/public'; export interface ObservabilityAppServices { http: HttpStart; @@ -36,4 +37,5 @@ export interface ObservabilityAppServices { triggersActionsUi: TriggersAndActionsUIPublicPluginStart; lens: LensPublicStart; + cases: CasesUiStart; } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx new file mode 100644 index 0000000000000..619ea0d21ae15 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx @@ -0,0 +1,54 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '../rtl_helpers'; +import { fireEvent } from '@testing-library/dom'; +import { AddToCaseAction } from './add_to_case_action'; + +describe('AddToCaseAction', function () { + it('should render properly', async function () { + const { findByText } = render( + + ); + expect(await findByText('Add to case')).toBeInTheDocument(); + }); + + it('should be able to click add to case button', async function () { + const initSeries = { + data: { + 'uptime-pings-histogram': { + dataType: 'synthetics' as const, + reportType: 'kpi-over-time' as const, + breakdown: 'monitor.status', + time: { from: 'now-15m', to: 'now' }, + }, + }, + }; + + const { findByText, core } = render( + , + { initSeries } + ); + fireEvent.click(await findByText('Add to case')); + + expect(core?.cases?.getAllCasesSelectorModal).toHaveBeenCalledTimes(1); + expect(core?.cases?.getAllCasesSelectorModal).toHaveBeenCalledWith( + expect.objectContaining({ + createCaseNavigation: expect.objectContaining({ href: '/app/observability/cases/create' }), + owner: ['observability'], + userCanCrud: true, + }) + ); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.tsx new file mode 100644 index 0000000000000..4fa8deb2700d0 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.tsx @@ -0,0 +1,89 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useCallback } from 'react'; +import { toMountPoint, useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { ObservabilityAppServices } from '../../../../application/types'; +import { AllCasesSelectorModalProps } from '../../../../../../cases/public'; +import { TypedLensByValueInput } from '../../../../../../lens/public'; +import { useAddToCase } from '../hooks/use_add_to_case'; +import { Case, SubCase } from '../../../../../../cases/common'; +import { observabilityFeatureId } from '../../../../../common'; + +export interface AddToCaseProps { + timeRange: { from: string; to: string }; + lensAttributes: TypedLensByValueInput['attributes'] | null; +} + +export function AddToCaseAction({ lensAttributes, timeRange }: AddToCaseProps) { + const kServices = useKibana().services; + + const { cases, http } = kServices; + + const getToastText = useCallback( + (theCase) => toMountPoint(), + [http.basePath] + ); + + const { createCaseUrl, goToCreateCase, onCaseClicked, isCasesOpen, setIsCasesOpen, isSaving } = + useAddToCase({ + lensAttributes, + getToastText, + timeRange, + }); + + const getAllCasesSelectorModalProps: AllCasesSelectorModalProps = { + createCaseNavigation: { + href: createCaseUrl, + onClick: goToCreateCase, + }, + onRowClick: onCaseClicked, + userCanCrud: true, + owner: [observabilityFeatureId], + onClose: () => { + setIsCasesOpen(false); + }, + }; + + return ( + <> + { + if (lensAttributes) { + setIsCasesOpen(true); + } + }} + > + {i18n.translate('xpack.observability.expView.heading.addToCase', { + defaultMessage: 'Add to case', + })} + + {isCasesOpen && + lensAttributes && + cases.getAllCasesSelectorModal(getAllCasesSelectorModalProps)} + + ); +} + +function CaseToastText({ theCase, basePath }: { theCase: Case | SubCase; basePath: string }) { + return ( + + + + {i18n.translate('xpack.observability.expView.heading.addToCase.notification.viewCase', { + defaultMessage: 'View case', + })} + + + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx index 706ba546b2848..7adef4779ea94 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx @@ -14,6 +14,7 @@ import { DataViewLabels } from '../configurations/constants'; import { ObservabilityAppServices } from '../../../../application/types'; import { useSeriesStorage } from '../hooks/use_series_storage'; import { combineTimeRanges } from '../exploratory_view'; +import { AddToCaseAction } from './add_to_case_action'; interface Props { seriesId: string; @@ -56,6 +57,9 @@ export function ExploratoryViewHeader({ seriesId, lensAttributes }: Props) { + + + { + setData(result); + }, [result]); + + return ( + + Add new case + result.onCaseClicked({ id: 'test' } as any)}> + On case click + + + ); + } + + const renderSetup = render(); + + return { setData, ...renderSetup }; + } + it('should return expected result', async function () { + const { setData, core, findByText } = setupTestComponent(); + + expect(setData).toHaveBeenLastCalledWith({ + createCaseUrl: '/app/observability/cases/create', + goToCreateCase: expect.any(Function), + isCasesOpen: false, + isSaving: false, + onCaseClicked: expect.any(Function), + setIsCasesOpen: expect.any(Function), + }); + fireEvent.click(await findByText('Add new case')); + + expect(core.application?.navigateToApp).toHaveBeenCalledTimes(1); + expect(core.application?.navigateToApp).toHaveBeenCalledWith('observability', { + path: '/cases/create', + }); + + fireEvent.click(await findByText('On case click')); + + expect(core.http?.post).toHaveBeenCalledTimes(1); + expect(core.http?.post).toHaveBeenCalledWith('/api/cases/test/comments', { + body: '{"comment":"!{lens{\\"attributes\\":{\\"title\\":\\"Test lens attributes\\"},\\"timeRange\\":{\\"to\\":\\"now\\",\\"from\\":\\"now-5m\\"}}}","type":"user","owner":"observability"}', + }); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_add_to_case.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_add_to_case.ts new file mode 100644 index 0000000000000..5ec9e1d4ab4b5 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_add_to_case.ts @@ -0,0 +1,128 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useMemo, useState } from 'react'; +import { isEmpty } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { HttpSetup, MountPoint } from 'kibana/public'; +import { useKibana } from '../../../../utils/kibana_react'; +import { Case, SubCase } from '../../../../../../cases/common'; +import { TypedLensByValueInput } from '../../../../../../lens/public'; +import { AddToCaseProps } from '../header/add_to_case_action'; +import { observabilityFeatureId } from '../../../../../common'; + +const appendSearch = (search?: string) => + isEmpty(search) ? '' : `${search?.startsWith('?') ? search : `?${search}`}`; + +const getCreateCaseUrl = (search?: string | null) => + `/cases/create${appendSearch(search ?? undefined)}`; + +async function addToCase( + http: HttpSetup, + theCase: Case | SubCase, + attributes: TypedLensByValueInput['attributes'], + timeRange: { from: string; to: string } +) { + const apiPath = `/api/cases/${theCase?.id}/comments`; + + const vizPayload = { + attributes, + timeRange, + }; + + const payload = { + comment: `!{lens${JSON.stringify(vizPayload)}}`, + type: 'user', + owner: observabilityFeatureId, + }; + + return http.post(apiPath, { body: JSON.stringify(payload) }); +} + +export const useAddToCase = ({ + lensAttributes, + getToastText, + timeRange, +}: AddToCaseProps & { getToastText: (thaCase: Case | SubCase) => MountPoint }) => { + const [isSaving, setIsSaving] = useState(false); + const [isCasesOpen, setIsCasesOpen] = useState(false); + + const { + http, + application: { navigateToApp, getUrlForApp }, + notifications: { toasts }, + } = useKibana().services; + + const createCaseUrl = useMemo( + () => getUrlForApp(observabilityFeatureId) + getCreateCaseUrl(), + [getUrlForApp] + ); + + const goToCreateCase = useCallback( + async (ev) => { + ev.preventDefault(); + return navigateToApp(observabilityFeatureId, { + path: getCreateCaseUrl(), + }); + }, + [navigateToApp] + ); + + const onCaseClicked = useCallback( + (theCase?: Case | SubCase) => { + if (theCase && lensAttributes) { + setIsCasesOpen(false); + setIsSaving(true); + addToCase(http, theCase, lensAttributes, timeRange).then( + () => { + setIsSaving(false); + toasts.addSuccess( + { + title: i18n.translate( + 'xpack.observability.expView.heading.addToCase.notification', + { + defaultMessage: 'Successfully added visualization to the case: {caseTitle}', + values: { caseTitle: theCase.title }, + } + ), + text: getToastText(theCase), + }, + { + toastLifeTimeMs: 10000, + } + ); + }, + (error) => { + toasts.addError(error, { + title: i18n.translate( + 'xpack.observability.expView.heading.addToCase.notification.error', + { + defaultMessage: 'Failed to add visualization to the selected case.', + } + ), + }); + } + ); + } else { + navigateToApp(observabilityFeatureId, { + path: getCreateCaseUrl(), + openInNewTab: true, + }); + } + }, + [getToastText, http, lensAttributes, navigateToApp, timeRange, toasts] + ); + + return { + createCaseUrl, + goToCreateCase, + onCaseClicked, + isSaving, + isCasesOpen, + setIsCasesOpen, + }; +}; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/rtl_helpers.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/rtl_helpers.tsx index bc77a0520925f..a577a8df3e3d9 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/rtl_helpers.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/rtl_helpers.tsx @@ -39,6 +39,7 @@ import { createStubIndexPattern } from '../../../../../../../src/plugins/data/co import { AppDataType, UrlFilter } from './types'; import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; import { ListItem } from '../../../hooks/use_values_list'; +import { casesPluginMock } from '../../../../../cases/public/mocks'; interface KibanaProps { services?: KibanaServices; @@ -118,6 +119,7 @@ export const mockCore: () => Partial Date: Thu, 30 Sep 2021 13:59:28 +0200 Subject: [PATCH 06/51] Improve logging deprecations (#113362) * add title and i18n for logging deprecations * Split logging.events.request and logging.events.response deprecations * factorize * fix unit tests * fix label --- .../deprecation/core_deprecations.test.ts | 23 +- .../config/deprecation/core_deprecations.ts | 273 ++++++++++++------ 2 files changed, 198 insertions(+), 98 deletions(-) diff --git a/src/core/server/config/deprecation/core_deprecations.test.ts b/src/core/server/config/deprecation/core_deprecations.test.ts index 819d45129bfe5..51eb845599729 100644 --- a/src/core/server/config/deprecation/core_deprecations.test.ts +++ b/src/core/server/config/deprecation/core_deprecations.test.ts @@ -273,36 +273,27 @@ describe('core deprecations', () => { }); }); - describe('logging.events.request and logging.events.response', () => { - it('warns when request and response events are used', () => { - const { messages } = applyCoreDeprecations({ - logging: { events: { request: '*', response: '*' } }, - }); - expect(messages).toMatchInlineSnapshot(` - Array [ - "\\"logging.events.request\\" and \\"logging.events.response\\" have been deprecated and will be removed in 8.0. To access request and/or response data moving forward, please enable debug logs for the \\"http.server.response\\" context in your logging configuration. For more details, see https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx", - ] - `); - }); - - it('warns when only request event is used', () => { + describe('logging.events.request', () => { + it('warns when request event is used', () => { const { messages } = applyCoreDeprecations({ logging: { events: { request: '*' } }, }); expect(messages).toMatchInlineSnapshot(` Array [ - "\\"logging.events.request\\" and \\"logging.events.response\\" have been deprecated and will be removed in 8.0. To access request and/or response data moving forward, please enable debug logs for the \\"http.server.response\\" context in your logging configuration. For more details, see https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx", + "\\"logging.events.request\\" has been deprecated and will be removed in 8.0. To access request data moving forward, please enable debug logs for the \\"http.server.response\\" context in your logging configuration. For more details, see https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx", ] `); }); + }); - it('warns when only response event is used', () => { + describe('logging.events.response', () => { + it('warns when response event is used', () => { const { messages } = applyCoreDeprecations({ logging: { events: { response: '*' } }, }); expect(messages).toMatchInlineSnapshot(` Array [ - "\\"logging.events.request\\" and \\"logging.events.response\\" have been deprecated and will be removed in 8.0. To access request and/or response data moving forward, please enable debug logs for the \\"http.server.response\\" context in your logging configuration. For more details, see https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx", + "\\"logging.events.response\\" has been deprecated and will be removed in 8.0. To access response data moving forward, please enable debug logs for the \\"http.server.response\\" context in your logging configuration. For more details, see https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx", ] `); }); diff --git a/src/core/server/config/deprecation/core_deprecations.ts b/src/core/server/config/deprecation/core_deprecations.ts index 09d7fd505701b..12c0099fb98eb 100644 --- a/src/core/server/config/deprecation/core_deprecations.ts +++ b/src/core/server/config/deprecation/core_deprecations.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { i18n } from '@kbn/i18n'; import { ConfigDeprecationProvider, ConfigDeprecation } from '@kbn/config'; const kibanaPathConf: ConfigDeprecation = (settings, fromPath, addDeprecation) => { @@ -175,20 +176,36 @@ const serverHostZeroDeprecation: ConfigDeprecation = (settings, fromPath, addDep return settings; }; +const removeFromConfigStep = (setting: string) => { + return i18n.translate('core.deprecations.common.removeFromConfig', { + defaultMessage: `Remove "{setting}" from your kibana configuration.`, + values: { + setting, + }, + }); +}; + const opsLoggingEventDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => { if (settings.logging?.events?.ops) { addDeprecation({ documentationUrl: 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingevents', - message: - '"logging.events.ops" has been deprecated and will be removed ' + - 'in 8.0. To access ops data moving forward, please enable debug logs for the ' + - '"metrics.ops" context in your logging configuration. For more details, see ' + - 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx', + title: i18n.translate('core.deprecations.loggingEventsOps.deprecationTitle', { + defaultMessage: `Setting "logging.events.ops" is deprecated`, + }), + message: i18n.translate('core.deprecations.loggingEventsOps.deprecationMessage', { + defaultMessage: + '"logging.events.ops" has been deprecated and will be removed ' + + 'in 8.0. To access ops data moving forward, please enable debug logs for the ' + + '"metrics.ops" context in your logging configuration. For more details, see ' + + 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx', + }), correctiveActions: { manualSteps: [ - `Remove "logging.events.ops" from your kibana settings.`, - `Enable debug logs for the "metrics.ops" context in your logging configuration`, + removeFromConfigStep('logging.events.ops'), + i18n.translate('core.deprecations.loggingEventsOps.manualSteps2', { + defaultMessage: `Enable debug logs for the "metrics.ops" context in your logging configuration`, + }), ], }, }); @@ -196,29 +213,53 @@ const opsLoggingEventDeprecation: ConfigDeprecation = (settings, fromPath, addDe }; const requestLoggingEventDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => { - if (settings.logging?.events?.request || settings.logging?.events?.response) { - const removeConfigsSteps = []; - - if (settings.logging?.events?.request) { - removeConfigsSteps.push(`Remove "logging.events.request" from your kibana configs.`); - } - - if (settings.logging?.events?.response) { - removeConfigsSteps.push(`Remove "logging.events.response" from your kibana configs.`); - } + if (settings.logging?.events?.request) { + addDeprecation({ + documentationUrl: + 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingevents', + title: i18n.translate('core.deprecations.loggingEventsRequest.deprecationTitle', { + defaultMessage: `Setting "logging.events.request" is deprecated`, + }), + message: i18n.translate('core.deprecations.loggingEventsRequest.deprecationMessage', { + defaultMessage: + '"logging.events.request" has been deprecated and will be removed ' + + 'in 8.0. To access request data moving forward, please enable debug logs for the ' + + '"http.server.response" context in your logging configuration. For more details, see ' + + 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx', + }), + correctiveActions: { + manualSteps: [ + removeFromConfigStep('logging.events.request'), + i18n.translate('core.deprecations.loggingEventsRequest.manualSteps2', { + defaultMessage: `enable debug logs for the "http.server.response" context in your logging configuration.`, + }), + ], + }, + }); + } +}; +const responseLoggingEventDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => { + if (settings.logging?.events?.response) { addDeprecation({ documentationUrl: 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingevents', - message: - '"logging.events.request" and "logging.events.response" have been deprecated and will be removed ' + - 'in 8.0. To access request and/or response data moving forward, please enable debug logs for the ' + - '"http.server.response" context in your logging configuration. For more details, see ' + - 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx', + title: i18n.translate('core.deprecations.loggingEventsResponse.deprecationTitle', { + defaultMessage: `Setting "logging.events.response" is deprecated`, + }), + message: i18n.translate('core.deprecations.loggingEventsResponse.deprecationMessage', { + defaultMessage: + '"logging.events.response" has been deprecated and will be removed ' + + 'in 8.0. To access response data moving forward, please enable debug logs for the ' + + '"http.server.response" context in your logging configuration. For more details, see ' + + 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx', + }), correctiveActions: { manualSteps: [ - ...removeConfigsSteps, - `enable debug logs for the "http.server.response" context in your logging configuration.`, + removeFromConfigStep('logging.events.response'), + i18n.translate('core.deprecations.loggingEventsResponse.manualSteps2', { + defaultMessage: `enable debug logs for the "http.server.response" context in your logging configuration.`, + }), ], }, }); @@ -230,15 +271,22 @@ const timezoneLoggingDeprecation: ConfigDeprecation = (settings, fromPath, addDe addDeprecation({ documentationUrl: 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingtimezone', - message: - '"logging.timezone" has been deprecated and will be removed ' + - 'in 8.0. To set the timezone moving forward, please add a timezone date modifier to the log pattern ' + - 'in your logging configuration. For more details, see ' + - 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx', + title: i18n.translate('core.deprecations.loggingTimezone.deprecationTitle', { + defaultMessage: `Setting "logging.timezone" is deprecated`, + }), + message: i18n.translate('core.deprecations.loggingTimezone.deprecationMessage', { + defaultMessage: + '"logging.timezone" has been deprecated and will be removed ' + + 'in 8.0. To set the timezone moving forward, please add a timezone date modifier to the log pattern ' + + 'in your logging configuration. For more details, see ' + + 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx', + }), correctiveActions: { manualSteps: [ - `Remove "logging.timezone" from your kibana configs.`, - `To set the timezone add a timezone date modifier to the log pattern in your logging configuration.`, + removeFromConfigStep('logging.timezone'), + i18n.translate('core.deprecations.loggingTimezone.manualSteps2', { + defaultMessage: `To set the timezone add a timezone date modifier to the log pattern in your logging configuration.`, + }), ], }, }); @@ -250,15 +298,22 @@ const destLoggingDeprecation: ConfigDeprecation = (settings, fromPath, addDeprec addDeprecation({ documentationUrl: 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingdest', - message: - '"logging.dest" has been deprecated and will be removed ' + - 'in 8.0. To set the destination moving forward, you can use the "console" appender ' + - 'in your logging configuration or define a custom one. For more details, see ' + - 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx', + title: i18n.translate('core.deprecations.loggingDest.deprecationTitle', { + defaultMessage: `Setting "logging.dest" is deprecated`, + }), + message: i18n.translate('core.deprecations.loggingDest.deprecationMessage', { + defaultMessage: + '"logging.dest" has been deprecated and will be removed ' + + 'in 8.0. To set the destination moving forward, you can use the "console" appender ' + + 'in your logging configuration or define a custom one. For more details, see ' + + 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx', + }), correctiveActions: { manualSteps: [ - `Remove "logging.dest" from your kibana configs.`, - `To set the destination use the "console" appender in your logging configuration or define a custom one.`, + removeFromConfigStep('logging.dest'), + i18n.translate('core.deprecations.loggingDest.manualSteps2', { + defaultMessage: `To set the destination use the "console" appender in your logging configuration or define a custom one.`, + }), ], }, }); @@ -270,13 +325,20 @@ const quietLoggingDeprecation: ConfigDeprecation = (settings, fromPath, addDepre addDeprecation({ documentationUrl: 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingquiet', - message: - '"logging.quiet" has been deprecated and will be removed ' + - 'in 8.0. Moving forward, you can use "logging.root.level:error" in your logging configuration. ', + title: i18n.translate('core.deprecations.loggingQuiet.deprecationTitle', { + defaultMessage: `Setting "logging.quiet" is deprecated`, + }), + message: i18n.translate('core.deprecations.loggingQuiet.deprecationMessage', { + defaultMessage: + '"logging.quiet" has been deprecated and will be removed ' + + 'in 8.0. Moving forward, you can use "logging.root.level:error" in your logging configuration. ', + }), correctiveActions: { manualSteps: [ - `Remove "logging.quiet" from your kibana configs.`, - `Use "logging.root.level:error" in your logging configuration.`, + removeFromConfigStep('logging.quiet'), + i18n.translate('core.deprecations.loggingQuiet.manualSteps2', { + defaultMessage: `Use "logging.root.level:error" in your logging configuration.`, + }), ], }, }); @@ -288,13 +350,20 @@ const silentLoggingDeprecation: ConfigDeprecation = (settings, fromPath, addDepr addDeprecation({ documentationUrl: 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingsilent', - message: - '"logging.silent" has been deprecated and will be removed ' + - 'in 8.0. Moving forward, you can use "logging.root.level:off" in your logging configuration. ', + title: i18n.translate('core.deprecations.loggingSilent.deprecationTitle', { + defaultMessage: `Setting "logging.silent" is deprecated`, + }), + message: i18n.translate('core.deprecations.loggingSilent.deprecationMessage', { + defaultMessage: + '"logging.silent" has been deprecated and will be removed ' + + 'in 8.0. Moving forward, you can use "logging.root.level:off" in your logging configuration. ', + }), correctiveActions: { manualSteps: [ - `Remove "logging.silent" from your kibana configs.`, - `Use "logging.root.level:off" in your logging configuration.`, + removeFromConfigStep('logging.silent'), + i18n.translate('core.deprecations.loggingSilent.manualSteps2', { + defaultMessage: `Use "logging.root.level:off" in your logging configuration.`, + }), ], }, }); @@ -306,13 +375,20 @@ const verboseLoggingDeprecation: ConfigDeprecation = (settings, fromPath, addDep addDeprecation({ documentationUrl: 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingverbose', - message: - '"logging.verbose" has been deprecated and will be removed ' + - 'in 8.0. Moving forward, you can use "logging.root.level:all" in your logging configuration. ', + title: i18n.translate('core.deprecations.loggingVerbose.deprecationTitle', { + defaultMessage: `Setting "logging.verbose" is deprecated`, + }), + message: i18n.translate('core.deprecations.loggingVerbose.deprecationMessage', { + defaultMessage: + '"logging.verbose" has been deprecated and will be removed ' + + 'in 8.0. Moving forward, you can use "logging.root.level:all" in your logging configuration. ', + }), correctiveActions: { manualSteps: [ - `Remove "logging.verbose" from your kibana configs.`, - `Use "logging.root.level:all" in your logging configuration.`, + removeFromConfigStep('logging.verbose'), + i18n.translate('core.deprecations.loggingVerbose.manualSteps2', { + defaultMessage: `Use "logging.root.level:all" in your logging configuration.`, + }), ], }, }); @@ -328,17 +404,24 @@ const jsonLoggingDeprecation: ConfigDeprecation = (settings, fromPath, addDeprec addDeprecation({ documentationUrl: 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx', - message: - '"logging.json" has been deprecated and will be removed ' + - 'in 8.0. To specify log message format moving forward, ' + - 'you can configure the "appender.layout" property for every custom appender in your logging configuration. ' + - 'There is currently no default layout for custom appenders and each one must be declared explicitly. ' + - 'For more details, see ' + - 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx', + title: i18n.translate('core.deprecations.loggingJson.deprecationTitle', { + defaultMessage: `Setting "logging.json" is deprecated`, + }), + message: i18n.translate('core.deprecations.loggingJson.deprecationMessage', { + defaultMessage: + '"logging.json" has been deprecated and will be removed ' + + 'in 8.0. To specify log message format moving forward, ' + + 'you can configure the "appender.layout" property for every custom appender in your logging configuration. ' + + 'There is currently no default layout for custom appenders and each one must be declared explicitly. ' + + 'For more details, see ' + + 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx', + }), correctiveActions: { manualSteps: [ - `Remove "logging.json" from your kibana configs.`, - `Configure the "appender.layout" property for every custom appender in your logging configuration.`, + removeFromConfigStep('logging.json'), + i18n.translate('core.deprecations.loggingJson.manualSteps2', { + defaultMessage: `Configure the "appender.layout" property for every custom appender in your logging configuration.`, + }), ], }, }); @@ -350,15 +433,22 @@ const logRotateDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecat addDeprecation({ documentationUrl: 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#rolling-file-appender', - message: - '"logging.rotate" and sub-options have been deprecated and will be removed in 8.0. ' + - 'Moving forward, you can enable log rotation using the "rolling-file" appender for a logger ' + - 'in your logging configuration. For more details, see ' + - 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#rolling-file-appender', + title: i18n.translate('core.deprecations.loggingRotate.deprecationTitle', { + defaultMessage: `Setting "logging.rotate" is deprecated`, + }), + message: i18n.translate('core.deprecations.loggingRotate.deprecationMessage', { + defaultMessage: + '"logging.rotate" and sub-options have been deprecated and will be removed in 8.0. ' + + 'Moving forward, you can enable log rotation using the "rolling-file" appender for a logger ' + + 'in your logging configuration. For more details, see ' + + 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#rolling-file-appender', + }), correctiveActions: { manualSteps: [ - `Remove "logging.rotate" from your kibana configs.`, - `Enable log rotation using the "rolling-file" appender for a logger in your logging configuration.`, + removeFromConfigStep('logging.rotate'), + i18n.translate('core.deprecations.loggingRotate.manualSteps2', { + defaultMessage: `Enable log rotation using the "rolling-file" appender for a logger in your logging configuration.`, + }), ], }, }); @@ -370,13 +460,20 @@ const logEventsLogDeprecation: ConfigDeprecation = (settings, fromPath, addDepre addDeprecation({ documentationUrl: 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingevents', - message: - '"logging.events.log" has been deprecated and will be removed ' + - 'in 8.0. Moving forward, log levels can be customized on a per-logger basis using the new logging configuration.', + title: i18n.translate('core.deprecations.loggingEventsLog.deprecationTitle', { + defaultMessage: `Setting "logging.events.log" is deprecated`, + }), + message: i18n.translate('core.deprecations.loggingEventsLog.deprecationMessage', { + defaultMessage: + '"logging.events.log" has been deprecated and will be removed ' + + 'in 8.0. Moving forward, log levels can be customized on a per-logger basis using the new logging configuration.', + }), correctiveActions: { manualSteps: [ - `Remove "logging.events.log" from your kibana configs.`, - `Customize log levels can be per-logger using the new logging configuration.`, + removeFromConfigStep('logging.events.log'), + i18n.translate('core.deprecations.loggingEventsLog.manualSteps2', { + defaultMessage: `Customize log levels can be per-logger using the new logging configuration.`, + }), ], }, }); @@ -388,13 +485,20 @@ const logEventsErrorDeprecation: ConfigDeprecation = (settings, fromPath, addDep addDeprecation({ documentationUrl: 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingevents', - message: - '"logging.events.error" has been deprecated and will be removed ' + - 'in 8.0. Moving forward, you can use "logging.root.level: error" in your logging configuration.', + title: i18n.translate('core.deprecations.loggingEventsError.deprecationTitle', { + defaultMessage: `Setting "logging.events.error" is deprecated`, + }), + message: i18n.translate('core.deprecations.loggingEventsError.deprecationMessage', { + defaultMessage: + '"logging.events.error" has been deprecated and will be removed ' + + 'in 8.0. Moving forward, you can use "logging.root.level: error" in your logging configuration.', + }), correctiveActions: { manualSteps: [ - `Remove "logging.events.error" from your kibana configs.`, - `Use "logging.root.level: error" in your logging configuration.`, + removeFromConfigStep('logging.events.error'), + i18n.translate('core.deprecations.loggingEventsError.manualSteps2', { + defaultMessage: `Use "logging.root.level: error" in your logging configuration.`, + }), ], }, }); @@ -406,9 +510,14 @@ const logFilterDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecat addDeprecation({ documentationUrl: 'https://github.com/elastic/kibana/blob/master/src/core/server/logging/README.mdx#loggingfilter', - message: '"logging.filter" has been deprecated and will be removed in 8.0.', + title: i18n.translate('core.deprecations.loggingFilter.deprecationTitle', { + defaultMessage: `Setting "logging.filter" is deprecated`, + }), + message: i18n.translate('core.deprecations.loggingFilter.deprecationMessage', { + defaultMessage: '"logging.filter" has been deprecated and will be removed in 8.0.', + }), correctiveActions: { - manualSteps: [`Remove "logging.filter" from your kibana configs.`], + manualSteps: [removeFromConfigStep('logging.filter')], }, }); } @@ -417,7 +526,6 @@ const logFilterDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecat export const coreDeprecationProvider: ConfigDeprecationProvider = ({ unusedFromRoot, renameFromRoot, - rename, }) => [ unusedFromRoot('savedObjects.indexCheckTimeout'), unusedFromRoot('server.xsrf.token'), @@ -463,6 +571,7 @@ export const coreDeprecationProvider: ConfigDeprecationProvider = ({ serverHostZeroDeprecation, opsLoggingEventDeprecation, requestLoggingEventDeprecation, + responseLoggingEventDeprecation, timezoneLoggingDeprecation, destLoggingDeprecation, quietLoggingDeprecation, From 99c800fa95b9c98ac58caa689d53955e6ee5a84c Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 30 Sep 2021 13:17:57 +0100 Subject: [PATCH 07/51] skip flaky suite (#113496) --- x-pack/test/functional/apps/visualize/reporting.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/visualize/reporting.ts b/x-pack/test/functional/apps/visualize/reporting.ts index 6ad1069fade20..9491416f328eb 100644 --- a/x-pack/test/functional/apps/visualize/reporting.ts +++ b/x-pack/test/functional/apps/visualize/reporting.ts @@ -42,7 +42,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - describe('Print PDF button', () => { + // FLAKY: https://github.com/elastic/kibana/issues/113496 + describe.skip('Print PDF button', () => { it('is available if new', async () => { await PageObjects.common.navigateToUrl('visualize', 'new', { useActualUrl: true }); await PageObjects.visualize.clickAggBasedVisualizations(); From 79673b6c7831124648c1f65ac80600f98b6a292c Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 09:07:33 -0400 Subject: [PATCH 08/51] :bug: Fix label on horizontal chart for axis (#113452) (#113523) Co-authored-by: Marco Liberati --- .../lens/public/xy_visualization/visualization_helpers.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx index 22c3c7e895323..e6bf460adbd2c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx @@ -40,9 +40,9 @@ export function getAxisName( defaultMessage: 'Horizontal bottom axis', }); if (axis === 'yLeft') { - return isHorizontal ? horizontalTop : verticalLeft; + return isHorizontal ? horizontalBottom : verticalLeft; } - return isHorizontal ? horizontalBottom : verticalRight; + return isHorizontal ? horizontalTop : verticalRight; } // min requirement for the bug: From 6250c39143044fbc3bdc346562068588bdb7f675 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 10:16:23 -0400 Subject: [PATCH 09/51] [Security Solution] Add host isolation exceptions UI (#111253) (#113529) Co-authored-by: Esteban Beltran --- .../src/common/exception_list/index.ts | 2 + .../src/common/lists/index.test.ts | 6 +- .../src/index.ts | 6 + .../security_solution/common/constants.ts | 3 + .../public/app/deep_links/index.ts | 22 +-- .../public/app/home/home_navigations.ts | 8 + .../public/app/translations.ts | 6 + .../common/components/navigation/types.ts | 1 + .../index.test.tsx | 10 ++ .../use_navigation_items.tsx | 7 +- .../public/common/store/actions.ts | 4 +- .../public/management/common/breadcrumbs.ts | 2 + .../public/management/common/constants.ts | 2 + .../public/management/common/routing.ts | 50 ++++++ .../pages/event_filters/store/middleware.ts | 9 +- .../host_isolation_exceptions/constants.ts | 23 +++ .../pages/host_isolation_exceptions/index.tsx | 30 ++++ .../host_isolation_exceptions/service.ts | 66 ++++++++ .../host_isolation_exceptions/store/action.ts | 16 ++ .../store/builders.ts | 19 +++ .../store/middleware.test.ts | 142 ++++++++++++++++++ .../store/middleware.ts | 90 +++++++++++ .../store/reducer.test.ts | 44 ++++++ .../store/reducer.ts | 63 ++++++++ .../store/selector.ts | 75 +++++++++ .../pages/host_isolation_exceptions/types.ts | 23 +++ .../view/components/empty.tsx | 42 ++++++ .../host_isolation_exceptions/view/hooks.ts | 38 +++++ .../host_isolation_exceptions_list.test.tsx | 107 +++++++++++++ .../view/host_isolation_exceptions_list.tsx | 106 +++++++++++++ .../public/management/pages/index.tsx | 13 ++ .../pages/trusted_apps/store/middleware.ts | 5 +- .../public/management/store/middleware.ts | 6 + .../public/management/store/reducer.ts | 5 + .../public/management/types.ts | 3 + 35 files changed, 1030 insertions(+), 24 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/constants.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/index.tsx create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/service.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/action.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/builders.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/middleware.test.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/middleware.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/reducer.test.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/reducer.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/selector.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/types.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/empty.tsx create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/hooks.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.tsx diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list/index.ts index a68dc2fc76a96..7d1aa44e7d907 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list/index.ts +++ b/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list/index.ts @@ -12,6 +12,7 @@ export const exceptionListType = t.keyof({ detection: null, endpoint: null, endpoint_events: null, + endpoint_host_isolation_exceptions: null, }); export const exceptionListTypeOrUndefined = t.union([exceptionListType, t.undefined]); export type ExceptionListType = t.TypeOf; @@ -20,4 +21,5 @@ export enum ExceptionListTypeEnum { DETECTION = 'detection', ENDPOINT = 'endpoint', ENDPOINT_EVENTS = 'endpoint_events', + ENDPOINT_HOST_ISOLATION_EXCEPTIONS = 'endpoint_host_isolation_exceptions', } diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.test.ts index 83e75a924f436..b4de979b19a97 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.test.ts +++ b/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.test.ts @@ -86,7 +86,7 @@ describe('Lists', () => { const message = pipe(decoded, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint" | "endpoint_events", namespace_type: "agnostic" | "single" |}>"', + 'Invalid value "1" supplied to "Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint" | "endpoint_events" | "endpoint_host_isolation_exceptions", namespace_type: "agnostic" | "single" |}>"', ]); expect(message.schema).toEqual({}); }); @@ -117,8 +117,8 @@ describe('Lists', () => { const message = pipe(decoded, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "(Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint" | "endpoint_events", namespace_type: "agnostic" | "single" |}> | undefined)"', - 'Invalid value "[1]" supplied to "(Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint" | "endpoint_events", namespace_type: "agnostic" | "single" |}> | undefined)"', + 'Invalid value "1" supplied to "(Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint" | "endpoint_events" | "endpoint_host_isolation_exceptions", namespace_type: "agnostic" | "single" |}> | undefined)"', + 'Invalid value "[1]" supplied to "(Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint" | "endpoint_events" | "endpoint_host_isolation_exceptions", namespace_type: "agnostic" | "single" |}> | undefined)"', ]); expect(message.schema).toEqual({}); }); diff --git a/packages/kbn-securitysolution-list-constants/src/index.ts b/packages/kbn-securitysolution-list-constants/src/index.ts index dae414aad0deb..8f5ea4668e00a 100644 --- a/packages/kbn-securitysolution-list-constants/src/index.ts +++ b/packages/kbn-securitysolution-list-constants/src/index.ts @@ -70,3 +70,9 @@ export const ENDPOINT_EVENT_FILTERS_LIST_NAME = 'Endpoint Security Event Filters /** Description of event filters agnostic list */ export const ENDPOINT_EVENT_FILTERS_LIST_DESCRIPTION = 'Endpoint Security Event Filters List'; + +export const ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID = 'endpoint_host_isolation_exceptions'; +export const ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_NAME = + 'Endpoint Security Host Isolation Exceptions List'; +export const ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_DESCRIPTION = + 'Endpoint Security Host Isolation Exceptions List'; diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index c88f5c7abd7ae..d77a555991df8 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -76,6 +76,7 @@ export enum SecurityPageName { detections = 'detections', endpoints = 'endpoints', eventFilters = 'event_filters', + hostIsolationExceptions = 'host_isolation_exceptions', events = 'events', exceptions = 'exceptions', explore = 'explore', @@ -113,6 +114,7 @@ export const MANAGEMENT_PATH = '/administration'; export const ENDPOINTS_PATH = `${MANAGEMENT_PATH}/endpoints`; export const TRUSTED_APPS_PATH = `${MANAGEMENT_PATH}/trusted_apps`; export const EVENT_FILTERS_PATH = `${MANAGEMENT_PATH}/event_filters`; +export const HOST_ISOLATION_EXCEPTIONS_PATH = `${MANAGEMENT_PATH}/host_isolation_exceptions`; export const APP_OVERVIEW_PATH = `${APP_PATH}${OVERVIEW_PATH}`; export const APP_MANAGEMENT_PATH = `${APP_PATH}${MANAGEMENT_PATH}`; @@ -129,6 +131,7 @@ export const APP_CASES_PATH = `${APP_PATH}${CASES_PATH}`; export const APP_ENDPOINTS_PATH = `${APP_PATH}${ENDPOINTS_PATH}`; export const APP_TRUSTED_APPS_PATH = `${APP_PATH}${TRUSTED_APPS_PATH}`; export const APP_EVENT_FILTERS_PATH = `${APP_PATH}${EVENT_FILTERS_PATH}`; +export const APP_HOST_ISOLATION_EXCEPTIONS_PATH = `${APP_PATH}${HOST_ISOLATION_EXCEPTIONS_PATH}`; /** The comma-delimited list of Elasticsearch indices from which the SIEM app collects events */ export const DEFAULT_INDEX_PATTERN = [ diff --git a/x-pack/plugins/security_solution/public/app/deep_links/index.ts b/x-pack/plugins/security_solution/public/app/deep_links/index.ts index 277c99217f32b..e8d4f5d09e5f7 100644 --- a/x-pack/plugins/security_solution/public/app/deep_links/index.ts +++ b/x-pack/plugins/security_solution/public/app/deep_links/index.ts @@ -30,6 +30,10 @@ import { CASE, MANAGE, UEBA, + HOST_ISOLATION_EXCEPTIONS, + EVENT_FILTERS, + TRUSTED_APPLICATIONS, + ENDPOINTS, } from '../translations'; import { OVERVIEW_PATH, @@ -44,6 +48,7 @@ import { TRUSTED_APPS_PATH, EVENT_FILTERS_PATH, UEBA_PATH, + HOST_ISOLATION_EXCEPTIONS_PATH, } from '../../../common/constants'; import { ExperimentalFeatures } from '../../../common/experimental_features'; @@ -313,26 +318,25 @@ export const securitySolutionsDeepLinks: AppDeepLink[] = [ { id: SecurityPageName.endpoints, navLinkStatus: AppNavLinkStatus.visible, - title: i18n.translate('xpack.securitySolution.search.administration.endpoints', { - defaultMessage: 'Endpoints', - }), + title: ENDPOINTS, order: 9006, path: ENDPOINTS_PATH, }, { id: SecurityPageName.trustedApps, - title: i18n.translate('xpack.securitySolution.search.administration.trustedApps', { - defaultMessage: 'Trusted applications', - }), + title: TRUSTED_APPLICATIONS, path: TRUSTED_APPS_PATH, }, { id: SecurityPageName.eventFilters, - title: i18n.translate('xpack.securitySolution.search.administration.eventFilters', { - defaultMessage: 'Event filters', - }), + title: EVENT_FILTERS, path: EVENT_FILTERS_PATH, }, + { + id: SecurityPageName.hostIsolationExceptions, + title: HOST_ISOLATION_EXCEPTIONS, + path: HOST_ISOLATION_EXCEPTIONS_PATH, + }, ], }, ]; diff --git a/x-pack/plugins/security_solution/public/app/home/home_navigations.ts b/x-pack/plugins/security_solution/public/app/home/home_navigations.ts index 686dafca76d99..38c7ab06a52d0 100644 --- a/x-pack/plugins/security_solution/public/app/home/home_navigations.ts +++ b/x-pack/plugins/security_solution/public/app/home/home_navigations.ts @@ -26,6 +26,7 @@ import { APP_EVENT_FILTERS_PATH, APP_UEBA_PATH, SecurityPageName, + APP_HOST_ISOLATION_EXCEPTIONS_PATH, } from '../../../common/constants'; export const navTabs: SecurityNav = { @@ -120,6 +121,13 @@ export const navTabs: SecurityNav = { disabled: false, urlKey: 'administration', }, + [SecurityPageName.hostIsolationExceptions]: { + id: SecurityPageName.hostIsolationExceptions, + name: i18n.HOST_ISOLATION_EXCEPTIONS, + href: APP_HOST_ISOLATION_EXCEPTIONS_PATH, + disabled: false, + urlKey: 'administration', + }, }; export const securityNavGroup: SecurityNavGroup = { diff --git a/x-pack/plugins/security_solution/public/app/translations.ts b/x-pack/plugins/security_solution/public/app/translations.ts index c3cf11f35211e..da680bf45dc8d 100644 --- a/x-pack/plugins/security_solution/public/app/translations.ts +++ b/x-pack/plugins/security_solution/public/app/translations.ts @@ -62,6 +62,12 @@ export const EVENT_FILTERS = i18n.translate( } ); +export const HOST_ISOLATION_EXCEPTIONS = i18n.translate( + 'xpack.securitySolution.search.administration.hostIsolationExceptions', + { + defaultMessage: 'Host Isolation Exceptions', + } +); export const DETECT = i18n.translate('xpack.securitySolution.navigation.detect', { defaultMessage: 'Detect', }); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/types.ts b/x-pack/plugins/security_solution/public/common/components/navigation/types.ts index 16bf53751515f..878ff074685e9 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/types.ts @@ -47,6 +47,7 @@ export type SecurityNavKey = | SecurityPageName.endpoints | SecurityPageName.eventFilters | SecurityPageName.exceptions + | SecurityPageName.hostIsolationExceptions | SecurityPageName.hosts | SecurityPageName.network | SecurityPageName.overview diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/index.test.tsx index 820d90087ce48..29861b1434147 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/index.test.tsx @@ -233,6 +233,16 @@ describe('useSecuritySolutionNavigation', () => { "name": "Event filters", "onClick": [Function], }, + Object { + "data-href": "securitySolution/host_isolation_exceptions", + "data-test-subj": "navigation-host_isolation_exceptions", + "disabled": false, + "href": "securitySolution/host_isolation_exceptions", + "id": "host_isolation_exceptions", + "isSelected": false, + "name": "Host Isolation Exceptions", + "onClick": [Function], + }, ], "name": "Manage", }, diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_navigation_items.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_navigation_items.tsx index 1630bc47fd0c3..976f15586b555 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_navigation_items.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_navigation_items.tsx @@ -83,7 +83,12 @@ function usePrimaryNavigationItemsToDisplay(navTabs: Record) { }, { ...securityNavGroup.manage, - items: [navTabs.endpoints, navTabs.trusted_apps, navTabs.event_filters], + items: [ + navTabs.endpoints, + navTabs.trusted_apps, + navTabs.event_filters, + navTabs.host_isolation_exceptions, + ], }, ], [navTabs, hasCasesReadPermissions] diff --git a/x-pack/plugins/security_solution/public/common/store/actions.ts b/x-pack/plugins/security_solution/public/common/store/actions.ts index 1987edc0e7307..ff6b0e80b78ad 100644 --- a/x-pack/plugins/security_solution/public/common/store/actions.ts +++ b/x-pack/plugins/security_solution/public/common/store/actions.ts @@ -9,6 +9,7 @@ import { EndpointAction } from '../../management/pages/endpoint_hosts/store/acti import { PolicyDetailsAction } from '../../management/pages/policy/store/policy_details'; import { TrustedAppsPageAction } from '../../management/pages/trusted_apps/store/action'; import { EventFiltersPageAction } from '../../management/pages/event_filters/store/action'; +import { HostIsolationExceptionsPageAction } from '../../management/pages/host_isolation_exceptions/store/action'; export { appActions } from './app'; export { dragAndDropActions } from './drag_and_drop'; @@ -21,4 +22,5 @@ export type AppAction = | RoutingAction | PolicyDetailsAction | TrustedAppsPageAction - | EventFiltersPageAction; + | EventFiltersPageAction + | HostIsolationExceptionsPageAction; diff --git a/x-pack/plugins/security_solution/public/management/common/breadcrumbs.ts b/x-pack/plugins/security_solution/public/management/common/breadcrumbs.ts index 9c3d781f514e9..ffda54d0deda1 100644 --- a/x-pack/plugins/security_solution/public/management/common/breadcrumbs.ts +++ b/x-pack/plugins/security_solution/public/management/common/breadcrumbs.ts @@ -9,12 +9,14 @@ import { ChromeBreadcrumb } from 'kibana/public'; import { AdministrationSubTab } from '../types'; import { ENDPOINTS_TAB, EVENT_FILTERS_TAB, POLICIES_TAB, TRUSTED_APPS_TAB } from './translations'; import { AdministrationRouteSpyState } from '../../common/utils/route/types'; +import { HOST_ISOLATION_EXCEPTIONS } from '../../app/translations'; const TabNameMappedToI18nKey: Record = { [AdministrationSubTab.endpoints]: ENDPOINTS_TAB, [AdministrationSubTab.policies]: POLICIES_TAB, [AdministrationSubTab.trustedApps]: TRUSTED_APPS_TAB, [AdministrationSubTab.eventFilters]: EVENT_FILTERS_TAB, + [AdministrationSubTab.hostIsolationExceptions]: HOST_ISOLATION_EXCEPTIONS, }; export function getBreadcrumbs(params: AdministrationRouteSpyState): ChromeBreadcrumb[] { diff --git a/x-pack/plugins/security_solution/public/management/common/constants.ts b/x-pack/plugins/security_solution/public/management/common/constants.ts index 01569eae59c12..ad17522a8130f 100644 --- a/x-pack/plugins/security_solution/public/management/common/constants.ts +++ b/x-pack/plugins/security_solution/public/management/common/constants.ts @@ -17,6 +17,7 @@ export const MANAGEMENT_ROUTING_POLICY_DETAILS_TRUSTED_APPS_PATH = `${MANAGEMENT export const MANAGEMENT_ROUTING_POLICY_DETAILS_PATH_OLD = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.policies})/:policyId`; export const MANAGEMENT_ROUTING_TRUSTED_APPS_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.trustedApps})`; export const MANAGEMENT_ROUTING_EVENT_FILTERS_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.eventFilters})`; +export const MANAGEMENT_ROUTING_HOST_ISOLATION_EXCEPTIONS_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.hostIsolationExceptions})`; // --[ STORE ]--------------------------------------------------------------------------- /** The SIEM global store namespace where the management state will be mounted */ @@ -29,6 +30,7 @@ export const MANAGEMENT_STORE_ENDPOINTS_NAMESPACE = 'endpoints'; export const MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE = 'trustedApps'; /** Namespace within the Management state where event filters page state is maintained */ export const MANAGEMENT_STORE_EVENT_FILTERS_NAMESPACE = 'eventFilters'; +export const MANAGEMENT_STORE_HOST_ISOLATION_EXCEPTIONS_NAMESPACE = 'hostIsolationExceptions'; export const MANAGEMENT_PAGE_SIZE_OPTIONS: readonly number[] = [10, 20, 50]; export const MANAGEMENT_DEFAULT_PAGE = 0; diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index 58fbd64faf8a6..ed33071006776 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -16,6 +16,7 @@ import { MANAGEMENT_PAGE_SIZE_OPTIONS, MANAGEMENT_ROUTING_ENDPOINTS_PATH, MANAGEMENT_ROUTING_EVENT_FILTERS_PATH, + MANAGEMENT_ROUTING_HOST_ISOLATION_EXCEPTIONS_PATH, MANAGEMENT_ROUTING_POLICIES_PATH, MANAGEMENT_ROUTING_POLICY_DETAILS_FORM_PATH, MANAGEMENT_ROUTING_POLICY_DETAILS_TRUSTED_APPS_PATH, @@ -26,6 +27,7 @@ import { appendSearch } from '../../common/components/link_to/helpers'; import { EndpointIndexUIQueryParams } from '../pages/endpoint_hosts/types'; import { TrustedAppsListPageLocation } from '../pages/trusted_apps/state'; import { EventFiltersPageLocation } from '../pages/event_filters/types'; +import { HostIsolationExceptionsPageLocation } from '../pages/host_isolation_exceptions/types'; import { PolicyDetailsArtifactsPageLocation } from '../pages/policy/types'; // Taken from: https://github.com/microsoft/TypeScript/issues/12936#issuecomment-559034150 @@ -200,6 +202,26 @@ const normalizeEventFiltersPageLocation = ( } }; +const normalizeHostIsolationExceptionsPageLocation = ( + location?: Partial +): Partial => { + if (location) { + return { + ...(!isDefaultOrMissing(location.page_index, MANAGEMENT_DEFAULT_PAGE) + ? { page_index: location.page_index } + : {}), + ...(!isDefaultOrMissing(location.page_size, MANAGEMENT_DEFAULT_PAGE_SIZE) + ? { page_size: location.page_size } + : {}), + ...(!isDefaultOrMissing(location.show, undefined) ? { show: location.show } : {}), + ...(!isDefaultOrMissing(location.id, undefined) ? { id: location.id } : {}), + ...(!isDefaultOrMissing(location.filter, '') ? { filter: location.filter } : ''), + }; + } else { + return {}; + } +}; + /** * Given an object with url params, and a given key, return back only the first param value (case multiples were defined) * @param query @@ -327,3 +349,31 @@ export const getEventFiltersListPath = (location?: Partial { + const showParamValue = extractFirstParamValue( + query, + 'show' + ) as HostIsolationExceptionsPageLocation['show']; + + return { + ...extractListPaginationParams(query), + show: + showParamValue && ['edit', 'create'].includes(showParamValue) ? showParamValue : undefined, + id: extractFirstParamValue(query, 'id'), + }; +}; + +export const getHostIsolationExceptionsListPath = ( + location?: Partial +): string => { + const path = generatePath(MANAGEMENT_ROUTING_HOST_ISOLATION_EXCEPTIONS_PATH, { + tabName: AdministrationSubTab.hostIsolationExceptions, + }); + + return `${path}${appendSearch( + querystring.stringify(normalizeHostIsolationExceptionsPageLocation(location)) + )}`; +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/middleware.ts index 60920a7420d16..0c90e21b49530 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/middleware.ts @@ -203,8 +203,7 @@ const checkIfEventFilterDataExist: MiddlewareActionHandler = async ( ) => { dispatch({ type: 'eventFiltersListPageDataExistsChanged', - // Ignore will be fixed with when AsyncResourceState is refactored (#830) - // @ts-ignore + // @ts-expect-error-next-line will be fixed with when AsyncResourceState is refactored (#830) payload: createLoadingResourceState(getListPageDataExistsState(getState())), }); @@ -232,9 +231,8 @@ const refreshListDataIfNeeded: MiddlewareActionHandler = async (store, eventFilt dispatch({ type: 'eventFiltersListPageDataChanged', payload: { - // Ignore will be fixed with when AsyncResourceState is refactored (#830) - // @ts-ignore type: 'LoadingResourceState', + // @ts-expect-error-next-line will be fixed with when AsyncResourceState is refactored (#830) previousState: getCurrentListPageDataState(state), }, }); @@ -300,8 +298,7 @@ const eventFilterDeleteEntry: MiddlewareActionHandler = async ( dispatch({ type: 'eventFilterDeleteStatusChanged', - // Ignore will be fixed with when AsyncResourceState is refactored (#830) - // @ts-ignore + // @ts-expect-error-next-line will be fixed with when AsyncResourceState is refactored (#830) payload: createLoadingResourceState(getDeletionState(state).status), }); diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/constants.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/constants.ts new file mode 100644 index 0000000000000..dab3b528a181b --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/constants.ts @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { ExceptionListType, ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { + ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_DESCRIPTION, + ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID, + ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_NAME, +} from '@kbn/securitysolution-list-constants'; + +export const HOST_ISOLATION_EXCEPTIONS_LIST_TYPE: ExceptionListType = + ExceptionListTypeEnum.ENDPOINT_HOST_ISOLATION_EXCEPTIONS; + +export const HOST_ISOLATION_EXCEPTIONS_LIST = { + name: ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_NAME, + namespace_type: 'agnostic', + description: ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_DESCRIPTION, + list_id: ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID, + type: HOST_ISOLATION_EXCEPTIONS_LIST_TYPE, +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/index.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/index.tsx new file mode 100644 index 0000000000000..7ed2adf8c94fa --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/index.tsx @@ -0,0 +1,30 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Switch, Route } from 'react-router-dom'; +import React, { memo } from 'react'; +import { MANAGEMENT_ROUTING_HOST_ISOLATION_EXCEPTIONS_PATH } from '../../common/constants'; +import { NotFoundPage } from '../../../app/404'; +import { HostIsolationExceptionsList } from './view/host_isolation_exceptions_list'; + +/** + * Provides the routing container for the hosts related views + */ +export const HostIsolationExceptionsContainer = memo(() => { + return ( + + + + + ); +}); + +HostIsolationExceptionsContainer.displayName = 'HostIsolationExceptionsContainer'; diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/service.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/service.ts new file mode 100644 index 0000000000000..85545303c7df0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/service.ts @@ -0,0 +1,66 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + ExceptionListItemSchema, + FoundExceptionListItemSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import { ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID } from '@kbn/securitysolution-list-constants'; +import { HttpStart } from 'kibana/public'; +import { EXCEPTION_LIST_ITEM_URL, EXCEPTION_LIST_URL } from '../event_filters/constants'; +import { HOST_ISOLATION_EXCEPTIONS_LIST } from './constants'; + +async function createHostIsolationExceptionList(http: HttpStart): Promise { + try { + await http.post(EXCEPTION_LIST_URL, { + body: JSON.stringify(HOST_ISOLATION_EXCEPTIONS_LIST), + }); + } catch (err) { + // Ignore 409 errors. List already created + if (err.response?.status !== 409) { + throw err; + } + } +} + +let listExistsPromise: Promise; +async function ensureHostIsolationExceptionsListExists(http: HttpStart): Promise { + if (!listExistsPromise) { + listExistsPromise = createHostIsolationExceptionList(http); + } + await listExistsPromise; +} + +export async function getHostIsolationExceptionItems({ + http, + perPage, + page, + sortField, + sortOrder, + filter, +}: { + http: HttpStart; + page?: number; + perPage?: number; + sortField?: keyof ExceptionListItemSchema; + sortOrder?: 'asc' | 'desc'; + filter?: string; +}): Promise { + await ensureHostIsolationExceptionsListExists(http); + const entries: FoundExceptionListItemSchema = await http.get(`${EXCEPTION_LIST_ITEM_URL}/_find`, { + query: { + list_id: [ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID], + namespace_type: ['agnostic'], + page, + per_page: perPage, + sort_field: sortField, + sort_order: sortOrder, + filter, + }, + }); + return entries; +} diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/action.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/action.ts new file mode 100644 index 0000000000000..793c44ce79db2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/action.ts @@ -0,0 +1,16 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Action } from 'redux'; +import { HostIsolationExceptionsPageState } from '../types'; + +export type HostIsolationExceptionsPageDataChanged = + Action<'hostIsolationExceptionsPageDataChanged'> & { + payload: HostIsolationExceptionsPageState['entries']; + }; + +export type HostIsolationExceptionsPageAction = HostIsolationExceptionsPageDataChanged; diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/builders.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/builders.ts new file mode 100644 index 0000000000000..f5ea3c27bde7f --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/builders.ts @@ -0,0 +1,19 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { MANAGEMENT_DEFAULT_PAGE, MANAGEMENT_DEFAULT_PAGE_SIZE } from '../../../common/constants'; +import { createUninitialisedResourceState } from '../../../state'; +import { HostIsolationExceptionsPageState } from '../types'; + +export const initialHostIsolationExceptionsPageState = (): HostIsolationExceptionsPageState => ({ + entries: createUninitialisedResourceState(), + location: { + page_index: MANAGEMENT_DEFAULT_PAGE, + page_size: MANAGEMENT_DEFAULT_PAGE_SIZE, + filter: '', + }, +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/middleware.test.ts new file mode 100644 index 0000000000000..cde9d89443903 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/middleware.test.ts @@ -0,0 +1,142 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { applyMiddleware, createStore, Store } from 'redux'; +import { HOST_ISOLATION_EXCEPTIONS_PATH } from '../../../../../common/constants'; +import { coreMock } from '../../../../../../../../src/core/public/mocks'; +import { getFoundExceptionListItemSchemaMock } from '../../../../../../lists/common/schemas/response/found_exception_list_item_schema.mock'; +import { AppAction } from '../../../../common/store/actions'; +import { + createSpyMiddleware, + MiddlewareActionSpyHelper, +} from '../../../../common/store/test_utils'; +import { isFailedResourceState, isLoadedResourceState } from '../../../state'; +import { getHostIsolationExceptionItems } from '../service'; +import { HostIsolationExceptionsPageState } from '../types'; +import { initialHostIsolationExceptionsPageState } from './builders'; +import { createHostIsolationExceptionsPageMiddleware } from './middleware'; +import { hostIsolationExceptionsPageReducer } from './reducer'; +import { getListFetchError } from './selector'; + +jest.mock('../service'); +const getHostIsolationExceptionItemsMock = getHostIsolationExceptionItems as jest.Mock; + +const fakeCoreStart = coreMock.createStart({ basePath: '/mock' }); + +const createStoreSetup = () => { + const spyMiddleware = createSpyMiddleware(); + + return { + spyMiddleware, + store: createStore( + hostIsolationExceptionsPageReducer, + applyMiddleware( + createHostIsolationExceptionsPageMiddleware(fakeCoreStart), + spyMiddleware.actionSpyMiddleware + ) + ), + }; +}; + +describe('Host isolation exceptions middleware', () => { + let store: Store; + let spyMiddleware: MiddlewareActionSpyHelper; + let initialState: HostIsolationExceptionsPageState; + + beforeEach(() => { + initialState = initialHostIsolationExceptionsPageState(); + + const storeSetup = createStoreSetup(); + + store = storeSetup.store as Store; + spyMiddleware = storeSetup.spyMiddleware; + }); + + describe('initial state', () => { + it('sets initial state properly', async () => { + expect(createStoreSetup().store.getState()).toStrictEqual(initialState); + }); + }); + + describe('when on the List page', () => { + const changeUrl = (searchParams: string = '') => { + store.dispatch({ + type: 'userChangedUrl', + payload: { + pathname: HOST_ISOLATION_EXCEPTIONS_PATH, + search: searchParams, + hash: '', + key: 'miniMe', + }, + }); + }; + + beforeEach(() => { + getHostIsolationExceptionItemsMock.mockClear(); + getHostIsolationExceptionItemsMock.mockImplementation(getFoundExceptionListItemSchemaMock); + }); + + it.each([ + [undefined, undefined], + [3, 50], + ])( + 'should trigger api call to retrieve host isolation exceptions params page_index[%s] page_size[%s]', + async (pageIndex, perPage) => { + changeUrl((pageIndex && perPage && `?page_index=${pageIndex}&page_size=${perPage}`) || ''); + await spyMiddleware.waitForAction('hostIsolationExceptionsPageDataChanged', { + validate({ payload }) { + return isLoadedResourceState(payload); + }, + }); + + expect(getHostIsolationExceptionItemsMock).toHaveBeenCalledWith( + expect.objectContaining({ + page: (pageIndex ?? 0) + 1, + perPage: perPage ?? 10, + filter: undefined, + }) + ); + } + ); + + it('should clear up previous page and apply a filter configuration when a filter is used', async () => { + changeUrl('?filter=testMe'); + await spyMiddleware.waitForAction('hostIsolationExceptionsPageDataChanged', { + validate({ payload }) { + return isLoadedResourceState(payload); + }, + }); + expect(getHostIsolationExceptionItemsMock).toHaveBeenCalledWith( + expect.objectContaining({ + page: 1, + perPage: 10, + filter: + '(exception-list-agnostic.attributes.name:(*testMe*) OR exception-list-agnostic.attributes.description:(*testMe*) OR exception-list-agnostic.attributes.entries.value:(*testMe*))', + }) + ); + }); + + it('should dispatch a Failure if an API error was encountered', async () => { + getHostIsolationExceptionItemsMock.mockRejectedValue({ + body: { message: 'error message', statusCode: 500, error: 'Internal Server Error' }, + }); + + changeUrl(); + await spyMiddleware.waitForAction('hostIsolationExceptionsPageDataChanged', { + validate({ payload }) { + return isFailedResourceState(payload); + }, + }); + + expect(getListFetchError(store.getState())).toEqual({ + message: 'error message', + statusCode: 500, + error: 'Internal Server Error', + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/middleware.ts new file mode 100644 index 0000000000000..1df0ef229d2ef --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/middleware.ts @@ -0,0 +1,90 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FoundExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { CoreStart, HttpStart } from 'kibana/public'; +import { matchPath } from 'react-router-dom'; +import { AppLocation, Immutable } from '../../../../../common/endpoint/types'; +import { ImmutableMiddleware, ImmutableMiddlewareAPI } from '../../../../common/store'; +import { AppAction } from '../../../../common/store/actions'; +import { MANAGEMENT_ROUTING_HOST_ISOLATION_EXCEPTIONS_PATH } from '../../../common/constants'; +import { parseQueryFilterToKQL } from '../../../common/utils'; +import { + createFailedResourceState, + createLoadedResourceState, +} from '../../../state/async_resource_builders'; +import { getHostIsolationExceptionItems } from '../service'; +import { HostIsolationExceptionsPageState } from '../types'; +import { getCurrentListPageDataState, getCurrentLocation } from './selector'; + +export const SEARCHABLE_FIELDS: Readonly = [`name`, `description`, `entries.value`]; + +export function hostIsolationExceptionsMiddlewareFactory(coreStart: CoreStart) { + return createHostIsolationExceptionsPageMiddleware(coreStart); +} + +export const createHostIsolationExceptionsPageMiddleware = ( + coreStart: CoreStart +): ImmutableMiddleware => { + return (store) => (next) => async (action) => { + next(action); + + if (action.type === 'userChangedUrl' && isHostIsolationExceptionsPage(action.payload)) { + loadHostIsolationExceptionsList(store, coreStart.http); + } + }; +}; + +async function loadHostIsolationExceptionsList( + store: ImmutableMiddlewareAPI, + http: HttpStart +) { + const { dispatch } = store; + try { + const { + page_size: pageSize, + page_index: pageIndex, + filter, + } = getCurrentLocation(store.getState()); + const query = { + http, + page: pageIndex + 1, + perPage: pageSize, + filter: parseQueryFilterToKQL(filter, SEARCHABLE_FIELDS) || undefined, + }; + + dispatch({ + type: 'hostIsolationExceptionsPageDataChanged', + payload: { + type: 'LoadingResourceState', + // @ts-expect-error-next-line will be fixed with when AsyncResourceState is refactored (#830) + previousState: getCurrentListPageDataState(store.getState()), + }, + }); + + const entries = await getHostIsolationExceptionItems(query); + + dispatch({ + type: 'hostIsolationExceptionsPageDataChanged', + payload: createLoadedResourceState(entries), + }); + } catch (error) { + dispatch({ + type: 'hostIsolationExceptionsPageDataChanged', + payload: createFailedResourceState(error.body ?? error), + }); + } +} + +function isHostIsolationExceptionsPage(location: Immutable) { + return ( + matchPath(location.pathname ?? '', { + path: MANAGEMENT_ROUTING_HOST_ISOLATION_EXCEPTIONS_PATH, + exact: true, + }) !== null + ); +} diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/reducer.test.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/reducer.test.ts new file mode 100644 index 0000000000000..211b03f36d965 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/reducer.test.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { UserChangedUrl } from '../../../../common/store/routing/action'; +import { HostIsolationExceptionsPageState } from '../types'; +import { initialHostIsolationExceptionsPageState } from './builders'; +import { HOST_ISOLATION_EXCEPTIONS_PATH } from '../../../../../common/constants'; +import { hostIsolationExceptionsPageReducer } from './reducer'; +import { getCurrentLocation } from './selector'; + +describe('Host Isolation Exceptions Reducer', () => { + let initialState: HostIsolationExceptionsPageState; + + beforeEach(() => { + initialState = initialHostIsolationExceptionsPageState(); + }); + + describe('UserChangedUrl', () => { + const userChangedUrlAction = ( + search = '', + pathname = HOST_ISOLATION_EXCEPTIONS_PATH + ): UserChangedUrl => ({ + type: 'userChangedUrl', + payload: { search, pathname, hash: '' }, + }); + + describe('When the url is set to host isolation exceptions', () => { + it('should set the default page size and index', () => { + const result = hostIsolationExceptionsPageReducer(initialState, userChangedUrlAction()); + expect(getCurrentLocation(result)).toEqual({ + filter: '', + id: undefined, + page_index: 0, + page_size: 10, + show: undefined, + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/reducer.ts new file mode 100644 index 0000000000000..1bce76c1bfd06 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/reducer.ts @@ -0,0 +1,63 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// eslint-disable-next-line import/no-nodejs-modules +import { parse } from 'querystring'; +import { matchPath } from 'react-router-dom'; +import { ImmutableReducer } from '../../../../common/store'; +import { AppAction } from '../../../../common/store/actions'; +import { AppLocation, Immutable } from '../../../../../common/endpoint/types'; +import { extractHostIsolationExceptionsPageLocation } from '../../../common/routing'; +import { HostIsolationExceptionsPageState } from '../types'; +import { initialHostIsolationExceptionsPageState } from './builders'; +import { MANAGEMENT_ROUTING_HOST_ISOLATION_EXCEPTIONS_PATH } from '../../../common/constants'; +import { UserChangedUrl } from '../../../../common/store/routing/action'; + +type StateReducer = ImmutableReducer; +type CaseReducer = ( + state: Immutable, + action: Immutable +) => Immutable; + +const isHostIsolationExceptionsPageLocation = (location: Immutable) => { + return ( + matchPath(location.pathname ?? '', { + path: MANAGEMENT_ROUTING_HOST_ISOLATION_EXCEPTIONS_PATH, + exact: true, + }) !== null + ); +}; + +export const hostIsolationExceptionsPageReducer: StateReducer = ( + state = initialHostIsolationExceptionsPageState(), + action +) => { + switch (action.type) { + case 'hostIsolationExceptionsPageDataChanged': { + return { + ...state, + entries: action.payload, + }; + } + case 'userChangedUrl': + return userChangedUrl(state, action); + } + return state; +}; + +const userChangedUrl: CaseReducer = (state, action) => { + if (isHostIsolationExceptionsPageLocation(action.payload)) { + const location = extractHostIsolationExceptionsPageLocation( + parse(action.payload.search.slice(1)) + ); + return { + ...state, + location, + }; + } + return state; +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/selector.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/selector.ts new file mode 100644 index 0000000000000..0ddfc0953263c --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/selector.ts @@ -0,0 +1,75 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Pagination } from '@elastic/eui'; +import { + ExceptionListItemSchema, + FoundExceptionListItemSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import { createSelector } from 'reselect'; +import { Immutable } from '../../../../../common/endpoint/types'; +import { ServerApiError } from '../../../../common/types'; +import { + MANAGEMENT_DEFAULT_PAGE_SIZE, + MANAGEMENT_PAGE_SIZE_OPTIONS, +} from '../../../common/constants'; +import { + getLastLoadedResourceState, + isFailedResourceState, + isLoadingResourceState, +} from '../../../state/async_resource_state'; +import { HostIsolationExceptionsPageState } from '../types'; + +type StoreState = Immutable; +type HostIsolationExceptionsSelector = (state: StoreState) => T; + +export const getCurrentListPageState: HostIsolationExceptionsSelector = (state) => { + return state; +}; + +export const getCurrentListPageDataState: HostIsolationExceptionsSelector = ( + state +) => state.entries; + +const getListApiSuccessResponse: HostIsolationExceptionsSelector< + Immutable | undefined +> = createSelector(getCurrentListPageDataState, (listPageData) => { + return getLastLoadedResourceState(listPageData)?.data; +}); + +export const getListItems: HostIsolationExceptionsSelector> = + createSelector(getListApiSuccessResponse, (apiResponseData) => { + return apiResponseData?.data || []; + }); + +export const getListPagination: HostIsolationExceptionsSelector = createSelector( + getListApiSuccessResponse, + // memoized via `reselect` until the API response changes + (response) => { + return { + totalItemCount: response?.total ?? 0, + pageSize: response?.per_page ?? MANAGEMENT_DEFAULT_PAGE_SIZE, + pageSizeOptions: [...MANAGEMENT_PAGE_SIZE_OPTIONS], + pageIndex: (response?.page ?? 1) - 1, + }; + } +); + +export const getListIsLoading: HostIsolationExceptionsSelector = createSelector( + getCurrentListPageDataState, + (listDataState) => isLoadingResourceState(listDataState) +); + +export const getListFetchError: HostIsolationExceptionsSelector< + Immutable | undefined +> = createSelector(getCurrentListPageDataState, (listPageDataState) => { + return (isFailedResourceState(listPageDataState) && listPageDataState.error) || undefined; +}); + +export const getCurrentLocation: HostIsolationExceptionsSelector = ( + state +) => state.location; diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/types.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/types.ts new file mode 100644 index 0000000000000..44f3d2a9df764 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/types.ts @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FoundExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { AsyncResourceState } from '../../state/async_resource_state'; + +export interface HostIsolationExceptionsPageLocation { + page_index: number; + page_size: number; + show?: 'create' | 'edit'; + /** Used for editing. The ID of the selected event filter */ + id?: string; + filter: string; +} + +export interface HostIsolationExceptionsPageState { + entries: AsyncResourceState; + location: HostIsolationExceptionsPageLocation; +} diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/empty.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/empty.tsx new file mode 100644 index 0000000000000..d7c512794173c --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/empty.tsx @@ -0,0 +1,42 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; +import styled, { css } from 'styled-components'; +import { EuiEmptyPrompt } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +const EmptyPrompt = styled(EuiEmptyPrompt)` + ${() => css` + max-width: 100%; + `} +`; + +export const HostIsolationExceptionsEmptyState = memo<{}>(() => { + return ( + + + + } + body={ + + } + /> + ); +}); + +HostIsolationExceptionsEmptyState.displayName = 'HostIsolationExceptionsEmptyState'; diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/hooks.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/hooks.ts new file mode 100644 index 0000000000000..db9ec467e7170 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/hooks.ts @@ -0,0 +1,38 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { useCallback } from 'react'; +import { useSelector } from 'react-redux'; +import { useHistory } from 'react-router-dom'; +import { State } from '../../../../common/store'; +import { + MANAGEMENT_STORE_GLOBAL_NAMESPACE, + MANAGEMENT_STORE_HOST_ISOLATION_EXCEPTIONS_NAMESPACE, +} from '../../../common/constants'; +import { getHostIsolationExceptionsListPath } from '../../../common/routing'; +import { getCurrentLocation } from '../store/selector'; +import { HostIsolationExceptionsPageLocation, HostIsolationExceptionsPageState } from '../types'; + +export function useHostIsolationExceptionsSelector( + selector: (state: HostIsolationExceptionsPageState) => R +): R { + return useSelector((state: State) => + selector( + state[MANAGEMENT_STORE_GLOBAL_NAMESPACE][MANAGEMENT_STORE_HOST_ISOLATION_EXCEPTIONS_NAMESPACE] + ) + ); +} + +export function useHostIsolationExceptionsNavigateCallback() { + const location = useHostIsolationExceptionsSelector(getCurrentLocation); + const history = useHistory(); + + return useCallback( + (args: Partial) => + history.push(getHostIsolationExceptionsListPath({ ...location, ...args })), + [history, location] + ); +} diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx new file mode 100644 index 0000000000000..53b8bc33c252f --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx @@ -0,0 +1,107 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { act } from '@testing-library/react'; +import { AppContextTestRender, createAppRootMockRenderer } from '../../../../common/mock/endpoint'; +import { HOST_ISOLATION_EXCEPTIONS_PATH } from '../../../../../common/constants'; +import { HostIsolationExceptionsList } from './host_isolation_exceptions_list'; +import { isFailedResourceState, isLoadedResourceState } from '../../../state'; +import { getHostIsolationExceptionItems } from '../service'; +import { getFoundExceptionListItemSchemaMock } from '../../../../../../lists/common/schemas/response/found_exception_list_item_schema.mock'; + +jest.mock('../service'); +const getHostIsolationExceptionItemsMock = getHostIsolationExceptionItems as jest.Mock; + +describe('When on the host isolation exceptions page', () => { + let render: () => ReturnType; + let renderResult: ReturnType; + let history: AppContextTestRender['history']; + let waitForAction: AppContextTestRender['middlewareSpy']['waitForAction']; + beforeEach(() => { + getHostIsolationExceptionItemsMock.mockReset(); + const mockedContext = createAppRootMockRenderer(); + ({ history } = mockedContext); + render = () => (renderResult = mockedContext.render()); + waitForAction = mockedContext.middlewareSpy.waitForAction; + + act(() => { + history.push(HOST_ISOLATION_EXCEPTIONS_PATH); + }); + }); + describe('When on the host isolation list page', () => { + const dataReceived = () => + act(async () => { + await waitForAction('hostIsolationExceptionsPageDataChanged', { + validate(action) { + return isLoadedResourceState(action.payload); + }, + }); + }); + describe('And no data exists', () => { + beforeEach(async () => { + getHostIsolationExceptionItemsMock.mockReturnValue({ + data: [], + page: 1, + per_page: 10, + total: 0, + }); + }); + + it('should show the Empty message', async () => { + render(); + await dataReceived(); + expect(renderResult.getByTestId('hostIsolationExceptionsEmpty')).toBeTruthy(); + }); + }); + describe('And data exists', () => { + beforeEach(async () => { + getHostIsolationExceptionItemsMock.mockImplementation(getFoundExceptionListItemSchemaMock); + }); + it('should show loading indicator while retrieving data', async () => { + let releaseApiResponse: (value?: unknown) => void; + + getHostIsolationExceptionItemsMock.mockReturnValue( + new Promise((resolve) => (releaseApiResponse = resolve)) + ); + render(); + + expect(renderResult.getByTestId('hostIsolationExceptionsContent-loader')).toBeTruthy(); + + const wasReceived = dataReceived(); + releaseApiResponse!(); + await wasReceived; + expect(renderResult.container.querySelector('.euiProgress')).toBeNull(); + }); + + it('should show items on the list', async () => { + render(); + await dataReceived(); + + expect(renderResult.getByTestId('hostIsolationExceptionsCard')).toBeTruthy(); + }); + + it('should show API error if one is encountered', async () => { + getHostIsolationExceptionItemsMock.mockImplementation(() => { + throw new Error('Server is too far away'); + }); + const errorDispatched = act(async () => { + await waitForAction('hostIsolationExceptionsPageDataChanged', { + validate(action) { + return isFailedResourceState(action.payload); + }, + }); + }); + render(); + await errorDispatched; + expect( + renderResult.getByTestId('hostIsolationExceptionsContent-error').textContent + ).toEqual(' Server is too far away'); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.tsx new file mode 100644 index 0000000000000..f6198e4e1aa54 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.tsx @@ -0,0 +1,106 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { i18n } from '@kbn/i18n'; +import React, { useCallback } from 'react'; +import { EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { ExceptionItem } from '../../../../common/components/exceptions/viewer/exception_item'; +import { + getCurrentLocation, + getListFetchError, + getListIsLoading, + getListItems, + getListPagination, +} from '../store/selector'; +import { + useHostIsolationExceptionsNavigateCallback, + useHostIsolationExceptionsSelector, +} from './hooks'; +import { PaginatedContent, PaginatedContentProps } from '../../../components/paginated_content'; +import { Immutable } from '../../../../../common/endpoint/types'; +import { AdministrationListPage } from '../../../components/administration_list_page'; +import { SearchExceptions } from '../../../components/search_exceptions'; +import { ArtifactEntryCard, ArtifactEntryCardProps } from '../../../components/artifact_entry_card'; +import { HostIsolationExceptionsEmptyState } from './components/empty'; + +type HostIsolationExceptionPaginatedContent = PaginatedContentProps< + Immutable, + typeof ExceptionItem +>; + +export const HostIsolationExceptionsList = () => { + const listItems = useHostIsolationExceptionsSelector(getListItems); + const pagination = useHostIsolationExceptionsSelector(getListPagination); + const isLoading = useHostIsolationExceptionsSelector(getListIsLoading); + const fetchError = useHostIsolationExceptionsSelector(getListFetchError); + const location = useHostIsolationExceptionsSelector(getCurrentLocation); + + const navigateCallback = useHostIsolationExceptionsNavigateCallback(); + + const handleOnSearch = useCallback( + (query: string) => { + navigateCallback({ filter: query }); + }, + [navigateCallback] + ); + + const handleItemComponentProps = (element: ExceptionListItemSchema): ArtifactEntryCardProps => ({ + item: element, + 'data-test-subj': `hostIsolationExceptionsCard`, + }); + + const handlePaginatedContentChange: HostIsolationExceptionPaginatedContent['onChange'] = + useCallback( + ({ pageIndex, pageSize }) => { + navigateCallback({ + page_index: pageIndex, + page_size: pageSize, + }); + }, + [navigateCallback] + ); + + return ( + + } + actions={[]} + > + + + + items={listItems} + ItemComponent={ArtifactEntryCard} + itemComponentProps={handleItemComponentProps} + onChange={handlePaginatedContentChange} + error={fetchError?.message} + loading={isLoading} + pagination={pagination} + contentClassName="host-isolation-exceptions-container" + data-test-subj="hostIsolationExceptionsContent" + noItemsMessage={} + /> + + ); +}; + +HostIsolationExceptionsList.displayName = 'HostIsolationExceptionsList'; diff --git a/x-pack/plugins/security_solution/public/management/pages/index.tsx b/x-pack/plugins/security_solution/public/management/pages/index.tsx index f348be6089923..51eb56d23d541 100644 --- a/x-pack/plugins/security_solution/public/management/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/index.tsx @@ -12,6 +12,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { MANAGEMENT_ROUTING_ENDPOINTS_PATH, MANAGEMENT_ROUTING_EVENT_FILTERS_PATH, + MANAGEMENT_ROUTING_HOST_ISOLATION_EXCEPTIONS_PATH, MANAGEMENT_ROUTING_POLICIES_PATH, MANAGEMENT_ROUTING_TRUSTED_APPS_PATH, } from '../common/constants'; @@ -25,6 +26,7 @@ import { SpyRoute } from '../../common/utils/route/spy_routes'; import { EventFiltersContainer } from './event_filters'; import { getEndpointListPath } from '../common/routing'; import { useUserPrivileges } from '../../common/components/user_privileges'; +import { HostIsolationExceptionsContainer } from './host_isolation_exceptions'; const NoPermissions = memo(() => { return ( @@ -79,6 +81,13 @@ const EventFilterTelemetry = () => ( ); +const HostIsolationExceptionsTelemetry = () => ( + + + + +); + export const ManagementContainer = memo(() => { const { loading, canAccessEndpointManagement } = useUserPrivileges().endpointPrivileges; @@ -97,6 +106,10 @@ export const ManagementContainer = memo(() => { + diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts index aa34550d75849..f772986bff146 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts @@ -412,11 +412,8 @@ const fetchEditTrustedAppIfNeeded = async ( dispatch({ type: 'trustedAppCreationEditItemStateChanged', payload: { - // No easy way to get around this that I can see. `previousState` does not - // seem to allow everything that `editItem` state can hold, so not even sure if using - // type guards would work here - // @ts-ignore type: 'LoadingResourceState', + // @ts-expect-error-next-line will be fixed with when AsyncResourceState is refactored (#830) previousState: editItemState(currentState)!, }, }); diff --git a/x-pack/plugins/security_solution/public/management/store/middleware.ts b/x-pack/plugins/security_solution/public/management/store/middleware.ts index d011a9dcb91a7..b67b4687da010 100644 --- a/x-pack/plugins/security_solution/public/management/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/store/middleware.ts @@ -16,11 +16,13 @@ import { MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE, MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE, MANAGEMENT_STORE_EVENT_FILTERS_NAMESPACE, + MANAGEMENT_STORE_HOST_ISOLATION_EXCEPTIONS_NAMESPACE, } from '../common/constants'; import { policyDetailsMiddlewareFactory } from '../pages/policy/store/policy_details'; import { endpointMiddlewareFactory } from '../pages/endpoint_hosts/store/middleware'; import { trustedAppsPageMiddlewareFactory } from '../pages/trusted_apps/store/middleware'; import { eventFiltersPageMiddlewareFactory } from '../pages/event_filters/store/middleware'; +import { hostIsolationExceptionsMiddlewareFactory } from '../pages/host_isolation_exceptions/store/middleware'; type ManagementSubStateKey = keyof State[typeof MANAGEMENT_STORE_GLOBAL_NAMESPACE]; @@ -50,5 +52,9 @@ export const managementMiddlewareFactory: SecuritySubPluginMiddlewareFactory = ( createSubStateSelector(MANAGEMENT_STORE_EVENT_FILTERS_NAMESPACE), eventFiltersPageMiddlewareFactory(coreStart, depsStart) ), + substateMiddlewareFactory( + createSubStateSelector(MANAGEMENT_STORE_HOST_ISOLATION_EXCEPTIONS_NAMESPACE), + hostIsolationExceptionsMiddlewareFactory(coreStart) + ), ]; }; diff --git a/x-pack/plugins/security_solution/public/management/store/reducer.ts b/x-pack/plugins/security_solution/public/management/store/reducer.ts index 662d2b4322bcb..677114a58d56e 100644 --- a/x-pack/plugins/security_solution/public/management/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/store/reducer.ts @@ -15,6 +15,7 @@ import { MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE, MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE, MANAGEMENT_STORE_EVENT_FILTERS_NAMESPACE, + MANAGEMENT_STORE_HOST_ISOLATION_EXCEPTIONS_NAMESPACE, } from '../common/constants'; import { ImmutableCombineReducers } from '../../common/store'; import { Immutable } from '../../../common/endpoint/types'; @@ -25,6 +26,8 @@ import { trustedAppsPageReducer } from '../pages/trusted_apps/store/reducer'; import { initialEventFiltersPageState } from '../pages/event_filters/store/builders'; import { eventFiltersPageReducer } from '../pages/event_filters/store/reducer'; import { initialEndpointPageState } from '../pages/endpoint_hosts/store/builders'; +import { initialHostIsolationExceptionsPageState } from '../pages/host_isolation_exceptions/store/builders'; +import { hostIsolationExceptionsPageReducer } from '../pages/host_isolation_exceptions/store/reducer'; const immutableCombineReducers: ImmutableCombineReducers = combineReducers; @@ -36,6 +39,7 @@ export const mockManagementState: Immutable = { [MANAGEMENT_STORE_ENDPOINTS_NAMESPACE]: initialEndpointPageState(), [MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE]: initialTrustedAppsPageState(), [MANAGEMENT_STORE_EVENT_FILTERS_NAMESPACE]: initialEventFiltersPageState(), + [MANAGEMENT_STORE_HOST_ISOLATION_EXCEPTIONS_NAMESPACE]: initialHostIsolationExceptionsPageState(), }; /** @@ -46,4 +50,5 @@ export const managementReducer = immutableCombineReducers({ [MANAGEMENT_STORE_ENDPOINTS_NAMESPACE]: endpointListReducer, [MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE]: trustedAppsPageReducer, [MANAGEMENT_STORE_EVENT_FILTERS_NAMESPACE]: eventFiltersPageReducer, + [MANAGEMENT_STORE_HOST_ISOLATION_EXCEPTIONS_NAMESPACE]: hostIsolationExceptionsPageReducer, }); diff --git a/x-pack/plugins/security_solution/public/management/types.ts b/x-pack/plugins/security_solution/public/management/types.ts index cadb3b91f66a6..8e5fc3e6cfe90 100644 --- a/x-pack/plugins/security_solution/public/management/types.ts +++ b/x-pack/plugins/security_solution/public/management/types.ts @@ -11,6 +11,7 @@ import { PolicyDetailsState } from './pages/policy/types'; import { EndpointState } from './pages/endpoint_hosts/types'; import { TrustedAppsListPageState } from './pages/trusted_apps/state'; import { EventFiltersListPageState } from './pages/event_filters/types'; +import { HostIsolationExceptionsPageState } from './pages/host_isolation_exceptions/types'; /** * The type for the management store global namespace. Used mostly internally to reference @@ -23,6 +24,7 @@ export type ManagementState = CombinedState<{ endpoints: EndpointState; trustedApps: TrustedAppsListPageState; eventFilters: EventFiltersListPageState; + hostIsolationExceptions: HostIsolationExceptionsPageState; }>; /** @@ -33,6 +35,7 @@ export enum AdministrationSubTab { policies = 'policy', trustedApps = 'trusted_apps', eventFilters = 'event_filters', + hostIsolationExceptions = 'host_isolation_exceptions', } /** From 161b6d08f78e07e53968a97c1725d6ff777c149c Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Thu, 30 Sep 2021 11:22:40 -0400 Subject: [PATCH 10/51] [Actions] Better enqueue test (#112434) (#113534) * Try and add logging here * Fix linting * Only this test * Better logging * More debugging * More debug * Try something different * Better way to do the test * Get this PR ready Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../plugins/alerts/server/action_types.ts | 1 + .../spaces_only/tests/actions/enqueue.ts | 16 ++++++---------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/action_types.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/action_types.ts index 2d880aa700cf1..2246bdc424264 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/action_types.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/action_types.ts @@ -213,6 +213,7 @@ function getNoAttemptsRateLimitedActionType() { }); return { status: 'error', + message: 'intentional failure from action', retry: new Date(params.retryAt), actionId: '', }; diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/enqueue.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/enqueue.ts index 3094269932640..93f6a73b7ce21 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/enqueue.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/enqueue.ts @@ -23,8 +23,7 @@ export default function ({ getService }: FtrProviderContext) { const retry = getService('retry'); const esTestIndexTool = new ESTestIndexTool(es, retry); - // Failing: See https://github.com/elastic/kibana/issues/111812 - describe.skip('enqueue', () => { + describe('enqueue', () => { const objectRemover = new ObjectRemover(supertest); before(async () => { @@ -170,21 +169,17 @@ export default function ({ getService }: FtrProviderContext) { 'task.taskType': 'actions:test.no-attempts-rate-limit', }, }, - { - term: { - 'task.status': 'running', - }, - }, ], }, }, }, }); - expect((runningSearchResult.body.hits.total as estypes.SearchTotalHits).value).to.eql(1); + const total = (runningSearchResult.body.hits.total as estypes.SearchTotalHits).value; + expect(total).to.eql(1); }); await retry.try(async () => { - const searchResult = await es.search({ + const runningSearchResult = await es.search({ index: '.kibana_task_manager', body: { query: { @@ -200,7 +195,8 @@ export default function ({ getService }: FtrProviderContext) { }, }, }); - expect((searchResult.body.hits.total as estypes.SearchTotalHits).value).to.eql(0); + const total = (runningSearchResult.body.hits.total as estypes.SearchTotalHits).value; + expect(total).to.eql(0); }); }); }); From 285bdebd1899f2fb340645dcd2ad7d499aecc585 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 12:32:59 -0400 Subject: [PATCH 11/51] [APM] Remove dot from legend on the error marker (#113437) (#113539) * Remove dot from legend on the error marker * Change indicator type to NodeElement Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Miriam <31922082+MiriamAparicio@users.noreply.github.com> --- .../shared/charts/Timeline/Marker/error_marker.tsx | 5 ++--- .../apm/public/components/shared/charts/Timeline/legend.tsx | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/error_marker.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/error_marker.tsx index b1e902957bfd7..305488b9d39d2 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/error_marker.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/error_marker.tsx @@ -100,14 +100,13 @@ export function ErrorMarker({ mark }: Props) { ( -
@
- )} + indicator={
@
} /> } /> React.ReactNode; + indicator?: React.ReactNode; } export function Legend({ @@ -79,7 +79,7 @@ export function Legend({ {...rest} > {indicator ? ( - indicator() + indicator ) : ( )} From 9e1c88443fd6980bf81f223bf56f45dc0b17713b Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 12:45:07 -0400 Subject: [PATCH 12/51] Bump prismjs from 1.24.0 to 1.25.0 (#113388) (#113540) Co-authored-by: Thomas Watson --- package.json | 1 + yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 6b6d475a8953b..730bd4106dad5 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "**/pdfkit/crypto-js": "4.0.0", "**/react-syntax-highlighter": "^15.3.1", "**/react-syntax-highlighter/**/highlight.js": "^10.4.1", + "**/refractor/prismjs": "~1.25.0", "**/trim": "1.0.1", "**/typescript": "4.1.3", "**/underscore": "^1.13.1" diff --git a/yarn.lock b/yarn.lock index 1342647667af1..c7b0ac6f80e46 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22042,10 +22042,10 @@ printj@~1.1.0: resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== -prismjs@^1.22.0, prismjs@~1.24.0: - version "1.24.0" - resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.24.0.tgz#0409c30068a6c52c89ef7f1089b3ca4de56be2ac" - integrity sha512-SqV5GRsNqnzCL8k5dfAjCNhUrF3pR0A9lTDSCUZeh/LIshheXJEaP0hwLz2t4XHivd2J/v2HR+gRnigzeKe3cQ== +prismjs@^1.22.0, prismjs@~1.24.0, prismjs@~1.25.0: + version "1.25.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.25.0.tgz#6f822df1bdad965734b310b315a23315cf999756" + integrity sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg== private@^0.1.8, private@~0.1.5: version "0.1.8" From 2fd5d20ec4534725d6e35721e1b09b2c28e21bcc Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Thu, 30 Sep 2021 13:13:54 -0400 Subject: [PATCH 13/51] Clean up task manager public setup contract to improve readability and API docs (#113415) (#113543) * Improve API docs for task manager * Update docs --- api_docs/apm.json | 50 ++- api_docs/apm.mdx | 2 +- api_docs/core.mdx | 2 +- api_docs/core_application.mdx | 2 +- api_docs/core_chrome.mdx | 2 +- api_docs/core_http.mdx | 2 +- api_docs/core_saved_objects.json | 15 + api_docs/core_saved_objects.mdx | 2 +- api_docs/custom_integrations.json | 54 ++- api_docs/custom_integrations.mdx | 2 +- api_docs/data.json | 12 - api_docs/deprecations_by_api.mdx | 1 + api_docs/deprecations_by_plugin.mdx | 7 +- api_docs/discover_enhanced.json | 2 +- api_docs/fleet.json | 41 +- api_docs/fleet.mdx | 2 +- api_docs/home.json | 26 +- api_docs/index_management.json | 56 ++- api_docs/index_management.mdx | 2 +- api_docs/kibana_legacy.json | 354 +----------------- api_docs/kibana_legacy.mdx | 2 +- api_docs/kibana_react.json | 2 +- api_docs/lens.json | 2 +- api_docs/observability.json | 28 ++ api_docs/observability.mdx | 2 +- api_docs/plugin_directory.mdx | 20 +- api_docs/reporting.json | 14 +- api_docs/saved_objects_management.json | 56 +++ api_docs/saved_objects_management.mdx | 2 +- api_docs/task_manager.json | 321 +++++++++++++++- api_docs/task_manager.mdx | 2 +- api_docs/url_forwarding.json | 4 +- x-pack/plugins/task_manager/server/index.ts | 9 +- x-pack/plugins/task_manager/server/plugin.ts | 9 +- .../server/task_type_dictionary.ts | 60 ++- 35 files changed, 709 insertions(+), 460 deletions(-) diff --git a/api_docs/apm.json b/api_docs/apm.json index 616a79e6b7b9d..feb2426c69268 100644 --- a/api_docs/apm.json +++ b/api_docs/apm.json @@ -186,7 +186,7 @@ "APMPluginSetupDependencies", ", \"data\" | \"cloud\" | \"security\" | \"home\" | \"features\" | \"fleet\" | \"ml\" | \"actions\" | \"usageCollection\" | \"apmOss\" | \"licensing\" | \"observability\" | \"ruleRegistry\" | \"spaces\" | \"taskManager\" | \"alerting\">) => { config$: ", "Observable", - "<{ 'apm_oss.transactionIndices': string; 'apm_oss.spanIndices': string; 'apm_oss.errorIndices': string; 'apm_oss.metricsIndices': string; 'apm_oss.sourcemapIndices': string; 'apm_oss.onboardingIndices': string; 'xpack.apm.serviceMapEnabled': boolean; 'xpack.apm.serviceMapFingerprintBucketSize': number; 'xpack.apm.serviceMapTraceIdBucketSize': number; 'xpack.apm.serviceMapFingerprintGlobalBucketSize': number; 'xpack.apm.serviceMapTraceIdGlobalBucketSize': number; 'xpack.apm.serviceMapMaxTracesPerRequest': number; 'xpack.apm.ui.enabled': boolean; 'xpack.apm.maxServiceEnvironments': number; 'xpack.apm.maxServiceSelection': number; 'xpack.apm.ui.maxTraceItems': number; 'xpack.apm.ui.transactionGroupBucketSize': number; 'xpack.apm.autocreateApmIndexPattern': boolean; 'xpack.apm.telemetryCollectionEnabled': boolean; 'xpack.apm.searchAggregatedTransactions': ", + "<{ 'apm_oss.transactionIndices': string; 'apm_oss.spanIndices': string; 'apm_oss.errorIndices': string; 'apm_oss.metricsIndices': string; 'apm_oss.sourcemapIndices': string; 'apm_oss.onboardingIndices': string; 'xpack.apm.serviceMapEnabled': boolean; 'xpack.apm.serviceMapFingerprintBucketSize': number; 'xpack.apm.serviceMapTraceIdBucketSize': number; 'xpack.apm.serviceMapFingerprintGlobalBucketSize': number; 'xpack.apm.serviceMapTraceIdGlobalBucketSize': number; 'xpack.apm.serviceMapMaxTracesPerRequest': number; 'xpack.apm.ui.enabled': boolean; 'xpack.apm.ui.maxTraceItems': number; 'xpack.apm.ui.transactionGroupBucketSize': number; 'xpack.apm.autocreateApmIndexPattern': boolean; 'xpack.apm.telemetryCollectionEnabled': boolean; 'xpack.apm.searchAggregatedTransactions': ", "SearchAggregatedTransactionSetting", "; 'xpack.apm.metricsInterval': number; 'xpack.apm.agent.migrations.enabled': boolean; }>; getApmIndices: () => Promise<", "ApmIndicesConfig", @@ -206,7 +206,11 @@ "InferSearchResponseOf", ">, ESSearchRequestOf, {}>>; }>; }" + ">, ESSearchRequestOf, {}>>; termsEnum(operationName: string, params: ", + "APMEventESTermsEnumRequest", + "): Promise<", + "TermsEnumResponse", + ">; }>; }" ], "path": "x-pack/plugins/apm/server/plugin.ts", "deprecated": false, @@ -327,7 +331,7 @@ "signature": [ "(apmOssConfig: Readonly<{} & { enabled: boolean; transactionIndices: string; spanIndices: string; errorIndices: string; metricsIndices: string; sourcemapIndices: string; onboardingIndices: string; indexPattern: string; fleetMode: boolean; }>, apmConfig: Readonly<{} & { enabled: boolean; serviceMapEnabled: boolean; serviceMapFingerprintBucketSize: number; serviceMapTraceIdBucketSize: number; serviceMapFingerprintGlobalBucketSize: number; serviceMapTraceIdGlobalBucketSize: number; serviceMapMaxTracesPerRequest: number; autocreateApmIndexPattern: boolean; ui: Readonly<{} & { enabled: boolean; transactionGroupBucketSize: number; maxTraceItems: number; }>; searchAggregatedTransactions: ", "SearchAggregatedTransactionSetting", - "; telemetryCollectionEnabled: boolean; metricsInterval: number; maxServiceEnvironments: number; maxServiceSelection: number; profilingEnabled: boolean; agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; }>) => { 'apm_oss.transactionIndices': string; 'apm_oss.spanIndices': string; 'apm_oss.errorIndices': string; 'apm_oss.metricsIndices': string; 'apm_oss.sourcemapIndices': string; 'apm_oss.onboardingIndices': string; 'xpack.apm.serviceMapEnabled': boolean; 'xpack.apm.serviceMapFingerprintBucketSize': number; 'xpack.apm.serviceMapTraceIdBucketSize': number; 'xpack.apm.serviceMapFingerprintGlobalBucketSize': number; 'xpack.apm.serviceMapTraceIdGlobalBucketSize': number; 'xpack.apm.serviceMapMaxTracesPerRequest': number; 'xpack.apm.ui.enabled': boolean; 'xpack.apm.maxServiceEnvironments': number; 'xpack.apm.maxServiceSelection': number; 'xpack.apm.ui.maxTraceItems': number; 'xpack.apm.ui.transactionGroupBucketSize': number; 'xpack.apm.autocreateApmIndexPattern': boolean; 'xpack.apm.telemetryCollectionEnabled': boolean; 'xpack.apm.searchAggregatedTransactions': ", + "; telemetryCollectionEnabled: boolean; metricsInterval: number; profilingEnabled: boolean; agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; }>) => { 'apm_oss.transactionIndices': string; 'apm_oss.spanIndices': string; 'apm_oss.errorIndices': string; 'apm_oss.metricsIndices': string; 'apm_oss.sourcemapIndices': string; 'apm_oss.onboardingIndices': string; 'xpack.apm.serviceMapEnabled': boolean; 'xpack.apm.serviceMapFingerprintBucketSize': number; 'xpack.apm.serviceMapTraceIdBucketSize': number; 'xpack.apm.serviceMapFingerprintGlobalBucketSize': number; 'xpack.apm.serviceMapTraceIdGlobalBucketSize': number; 'xpack.apm.serviceMapMaxTracesPerRequest': number; 'xpack.apm.ui.enabled': boolean; 'xpack.apm.ui.maxTraceItems': number; 'xpack.apm.ui.transactionGroupBucketSize': number; 'xpack.apm.autocreateApmIndexPattern': boolean; 'xpack.apm.telemetryCollectionEnabled': boolean; 'xpack.apm.searchAggregatedTransactions': ", "SearchAggregatedTransactionSetting", "; 'xpack.apm.metricsInterval': number; 'xpack.apm.agent.migrations.enabled': boolean; }" ], @@ -358,7 +362,7 @@ "signature": [ "Readonly<{} & { enabled: boolean; serviceMapEnabled: boolean; serviceMapFingerprintBucketSize: number; serviceMapTraceIdBucketSize: number; serviceMapFingerprintGlobalBucketSize: number; serviceMapTraceIdGlobalBucketSize: number; serviceMapMaxTracesPerRequest: number; autocreateApmIndexPattern: boolean; ui: Readonly<{} & { enabled: boolean; transactionGroupBucketSize: number; maxTraceItems: number; }>; searchAggregatedTransactions: ", "SearchAggregatedTransactionSetting", - "; telemetryCollectionEnabled: boolean; metricsInterval: number; maxServiceEnvironments: number; maxServiceSelection: number; profilingEnabled: boolean; agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; }>" + "; telemetryCollectionEnabled: boolean; metricsInterval: number; profilingEnabled: boolean; agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; }>" ], "path": "x-pack/plugins/apm/server/index.ts", "deprecated": false, @@ -436,7 +440,7 @@ "label": "config", "description": [], "signature": [ - "{ 'apm_oss.transactionIndices': string; 'apm_oss.spanIndices': string; 'apm_oss.errorIndices': string; 'apm_oss.metricsIndices': string; 'apm_oss.sourcemapIndices': string; 'apm_oss.onboardingIndices': string; 'xpack.apm.serviceMapEnabled': boolean; 'xpack.apm.serviceMapFingerprintBucketSize': number; 'xpack.apm.serviceMapTraceIdBucketSize': number; 'xpack.apm.serviceMapFingerprintGlobalBucketSize': number; 'xpack.apm.serviceMapTraceIdGlobalBucketSize': number; 'xpack.apm.serviceMapMaxTracesPerRequest': number; 'xpack.apm.ui.enabled': boolean; 'xpack.apm.maxServiceEnvironments': number; 'xpack.apm.maxServiceSelection': number; 'xpack.apm.ui.maxTraceItems': number; 'xpack.apm.ui.transactionGroupBucketSize': number; 'xpack.apm.autocreateApmIndexPattern': boolean; 'xpack.apm.telemetryCollectionEnabled': boolean; 'xpack.apm.searchAggregatedTransactions': ", + "{ 'apm_oss.transactionIndices': string; 'apm_oss.spanIndices': string; 'apm_oss.errorIndices': string; 'apm_oss.metricsIndices': string; 'apm_oss.sourcemapIndices': string; 'apm_oss.onboardingIndices': string; 'xpack.apm.serviceMapEnabled': boolean; 'xpack.apm.serviceMapFingerprintBucketSize': number; 'xpack.apm.serviceMapTraceIdBucketSize': number; 'xpack.apm.serviceMapFingerprintGlobalBucketSize': number; 'xpack.apm.serviceMapTraceIdGlobalBucketSize': number; 'xpack.apm.serviceMapMaxTracesPerRequest': number; 'xpack.apm.ui.enabled': boolean; 'xpack.apm.ui.maxTraceItems': number; 'xpack.apm.ui.transactionGroupBucketSize': number; 'xpack.apm.autocreateApmIndexPattern': boolean; 'xpack.apm.telemetryCollectionEnabled': boolean; 'xpack.apm.searchAggregatedTransactions': ", "SearchAggregatedTransactionSetting", "; 'xpack.apm.metricsInterval': number; 'xpack.apm.agent.migrations.enabled': boolean; }" ], @@ -787,7 +791,7 @@ "label": "APIEndpoint", "description": [], "signature": [ - "\"POST /api/apm/index_pattern/static\" | \"GET /api/apm/index_pattern/dynamic\" | \"GET /api/apm/environments\" | \"GET /api/apm/services/{serviceName}/errors\" | \"GET /api/apm/services/{serviceName}/errors/{groupId}\" | \"GET /api/apm/services/{serviceName}/errors/distribution\" | \"GET /api/apm/services/{serviceName}/metrics/charts\" | \"GET /api/apm/observability_overview\" | \"GET /api/apm/observability_overview/has_data\" | \"GET /api/apm/rum/client-metrics\" | \"GET /api/apm/rum-client/page-load-distribution\" | \"GET /api/apm/rum-client/page-load-distribution/breakdown\" | \"GET /api/apm/rum-client/page-view-trends\" | \"GET /api/apm/rum-client/services\" | \"GET /api/apm/rum-client/visitor-breakdown\" | \"GET /api/apm/rum-client/web-core-vitals\" | \"GET /api/apm/rum-client/long-task-metrics\" | \"GET /api/apm/rum-client/url-search\" | \"GET /api/apm/rum-client/js-errors\" | \"GET /api/apm/observability_overview/has_rum_data\" | \"GET /api/apm/service-map\" | \"GET /api/apm/service-map/service/{serviceName}\" | \"GET /api/apm/service-map/backend/{backendName}\" | \"GET /api/apm/services/{serviceName}/serviceNodes\" | \"GET /api/apm/services\" | \"GET /api/apm/services/detailed_statistics\" | \"GET /api/apm/services/{serviceName}/metadata/details\" | \"GET /api/apm/services/{serviceName}/metadata/icons\" | \"GET /api/apm/services/{serviceName}/agent\" | \"GET /api/apm/services/{serviceName}/transaction_types\" | \"GET /api/apm/services/{serviceName}/node/{serviceNodeName}/metadata\" | \"GET /api/apm/services/{serviceName}/annotation/search\" | \"POST /api/apm/services/{serviceName}/annotation\" | \"GET /api/apm/services/{serviceName}/error_groups/main_statistics\" | \"GET /api/apm/services/{serviceName}/error_groups/detailed_statistics\" | \"GET /api/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\" | \"GET /api/apm/services/{serviceName}/throughput\" | \"GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics\" | \"GET /api/apm/services/{serviceName}/service_overview_instances/detailed_statistics\" | \"GET /api/apm/services/{serviceName}/dependencies\" | \"GET /api/apm/services/{serviceName}/dependencies/breakdown\" | \"GET /api/apm/services/{serviceName}/profiling/timeline\" | \"GET /api/apm/services/{serviceName}/profiling/statistics\" | \"GET /api/apm/services/{serviceName}/alerts\" | \"GET /api/apm/services/{serviceName}/infrastructure\" | \"GET /api/apm/traces/{traceId}\" | \"GET /api/apm/traces\" | \"GET /api/apm/traces/{traceId}/root_transaction\" | \"GET /api/apm/transactions/{transactionId}\" | \"GET /api/apm/services/{serviceName}/transactions/groups/main_statistics\" | \"GET /api/apm/services/{serviceName}/transactions/groups/detailed_statistics\" | \"GET /api/apm/services/{serviceName}/transactions/charts/latency\" | \"GET /api/apm/services/{serviceName}/transactions/traces/samples\" | \"GET /api/apm/services/{serviceName}/transaction/charts/breakdown\" | \"GET /api/apm/services/{serviceName}/transactions/charts/error_rate\" | \"GET /api/apm/alerts/chart_preview/transaction_error_rate\" | \"GET /api/apm/alerts/chart_preview/transaction_duration\" | \"GET /api/apm/alerts/chart_preview/transaction_error_count\" | \"GET /api/apm/settings/agent-configuration\" | \"GET /api/apm/settings/agent-configuration/view\" | \"DELETE /api/apm/settings/agent-configuration\" | \"PUT /api/apm/settings/agent-configuration\" | \"POST /api/apm/settings/agent-configuration/search\" | \"GET /api/apm/settings/agent-configuration/services\" | \"GET /api/apm/settings/agent-configuration/environments\" | \"GET /api/apm/settings/agent-configuration/agent_name\" | \"GET /api/apm/settings/anomaly-detection/jobs\" | \"POST /api/apm/settings/anomaly-detection/jobs\" | \"GET /api/apm/settings/anomaly-detection/environments\" | \"GET /api/apm/settings/apm-index-settings\" | \"GET /api/apm/settings/apm-indices\" | \"POST /api/apm/settings/apm-indices/save\" | \"GET /api/apm/settings/custom_links/transaction\" | \"GET /api/apm/settings/custom_links\" | \"POST /api/apm/settings/custom_links\" | \"PUT /api/apm/settings/custom_links/{id}\" | \"DELETE /api/apm/settings/custom_links/{id}\" | \"GET /api/apm/sourcemaps\" | \"POST /api/apm/sourcemaps\" | \"DELETE /api/apm/sourcemaps/{id}\" | \"GET /api/apm/fleet/has_data\" | \"GET /api/apm/fleet/agents\" | \"POST /api/apm/fleet/apm_server_schema\" | \"GET /api/apm/fleet/apm_server_schema/unsupported\" | \"GET /api/apm/fleet/migration_check\" | \"POST /api/apm/fleet/cloud_apm_package_policy\" | \"GET /api/apm/backends/top_backends\" | \"GET /api/apm/backends/{backendName}/upstream_services\" | \"GET /api/apm/backends/{backendName}/metadata\" | \"GET /api/apm/backends/{backendName}/charts/latency\" | \"GET /api/apm/backends/{backendName}/charts/throughput\" | \"GET /api/apm/backends/{backendName}/charts/error_rate\" | \"GET /api/apm/fallback_to_transactions\" | \"GET /api/apm/has_data\"" + "\"POST /api/apm/index_pattern/static\" | \"GET /api/apm/index_pattern/dynamic\" | \"GET /api/apm/environments\" | \"GET /api/apm/services/{serviceName}/errors\" | \"GET /api/apm/services/{serviceName}/errors/{groupId}\" | \"GET /api/apm/services/{serviceName}/errors/distribution\" | \"GET /api/apm/services/{serviceName}/metrics/charts\" | \"GET /api/apm/observability_overview\" | \"GET /api/apm/observability_overview/has_data\" | \"GET /api/apm/rum/client-metrics\" | \"GET /api/apm/rum-client/page-load-distribution\" | \"GET /api/apm/rum-client/page-load-distribution/breakdown\" | \"GET /api/apm/rum-client/page-view-trends\" | \"GET /api/apm/rum-client/services\" | \"GET /api/apm/rum-client/visitor-breakdown\" | \"GET /api/apm/rum-client/web-core-vitals\" | \"GET /api/apm/rum-client/long-task-metrics\" | \"GET /api/apm/rum-client/url-search\" | \"GET /api/apm/rum-client/js-errors\" | \"GET /api/apm/observability_overview/has_rum_data\" | \"GET /api/apm/service-map\" | \"GET /api/apm/service-map/service/{serviceName}\" | \"GET /api/apm/service-map/backend/{backendName}\" | \"GET /api/apm/services/{serviceName}/serviceNodes\" | \"GET /api/apm/services\" | \"GET /api/apm/services/detailed_statistics\" | \"GET /api/apm/services/{serviceName}/metadata/details\" | \"GET /api/apm/services/{serviceName}/metadata/icons\" | \"GET /api/apm/services/{serviceName}/agent\" | \"GET /api/apm/services/{serviceName}/transaction_types\" | \"GET /api/apm/services/{serviceName}/node/{serviceNodeName}/metadata\" | \"GET /api/apm/services/{serviceName}/annotation/search\" | \"POST /api/apm/services/{serviceName}/annotation\" | \"GET /api/apm/services/{serviceName}/error_groups/main_statistics\" | \"GET /api/apm/services/{serviceName}/error_groups/detailed_statistics\" | \"GET /api/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\" | \"GET /api/apm/services/{serviceName}/throughput\" | \"GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics\" | \"GET /api/apm/services/{serviceName}/service_overview_instances/detailed_statistics\" | \"GET /api/apm/services/{serviceName}/dependencies\" | \"GET /api/apm/services/{serviceName}/dependencies/breakdown\" | \"GET /api/apm/services/{serviceName}/profiling/timeline\" | \"GET /api/apm/services/{serviceName}/profiling/statistics\" | \"GET /api/apm/services/{serviceName}/alerts\" | \"GET /api/apm/services/{serviceName}/infrastructure\" | \"GET /internal/apm/suggestions\" | \"GET /api/apm/traces/{traceId}\" | \"GET /api/apm/traces\" | \"GET /api/apm/traces/{traceId}/root_transaction\" | \"GET /api/apm/transactions/{transactionId}\" | \"GET /api/apm/services/{serviceName}/transactions/groups/main_statistics\" | \"GET /api/apm/services/{serviceName}/transactions/groups/detailed_statistics\" | \"GET /api/apm/services/{serviceName}/transactions/charts/latency\" | \"GET /api/apm/services/{serviceName}/transactions/traces/samples\" | \"GET /api/apm/services/{serviceName}/transaction/charts/breakdown\" | \"GET /api/apm/services/{serviceName}/transactions/charts/error_rate\" | \"GET /api/apm/alerts/chart_preview/transaction_error_rate\" | \"GET /api/apm/alerts/chart_preview/transaction_duration\" | \"GET /api/apm/alerts/chart_preview/transaction_error_count\" | \"GET /api/apm/settings/agent-configuration\" | \"GET /api/apm/settings/agent-configuration/view\" | \"DELETE /api/apm/settings/agent-configuration\" | \"PUT /api/apm/settings/agent-configuration\" | \"POST /api/apm/settings/agent-configuration/search\" | \"GET /api/apm/settings/agent-configuration/services\" | \"GET /api/apm/settings/agent-configuration/environments\" | \"GET /api/apm/settings/agent-configuration/agent_name\" | \"GET /api/apm/settings/anomaly-detection/jobs\" | \"POST /api/apm/settings/anomaly-detection/jobs\" | \"GET /api/apm/settings/anomaly-detection/environments\" | \"GET /api/apm/settings/apm-index-settings\" | \"GET /api/apm/settings/apm-indices\" | \"POST /api/apm/settings/apm-indices/save\" | \"GET /api/apm/settings/custom_links/transaction\" | \"GET /api/apm/settings/custom_links\" | \"POST /api/apm/settings/custom_links\" | \"PUT /api/apm/settings/custom_links/{id}\" | \"DELETE /api/apm/settings/custom_links/{id}\" | \"GET /api/apm/sourcemaps\" | \"POST /api/apm/sourcemaps\" | \"DELETE /api/apm/sourcemaps/{id}\" | \"GET /api/apm/fleet/has_data\" | \"GET /api/apm/fleet/agents\" | \"POST /api/apm/fleet/apm_server_schema\" | \"GET /api/apm/fleet/apm_server_schema/unsupported\" | \"GET /api/apm/fleet/migration_check\" | \"POST /api/apm/fleet/cloud_apm_package_policy\" | \"GET /api/apm/backends/top_backends\" | \"GET /api/apm/backends/{backendName}/upstream_services\" | \"GET /api/apm/backends/{backendName}/metadata\" | \"GET /api/apm/backends/{backendName}/charts/latency\" | \"GET /api/apm/backends/{backendName}/charts/throughput\" | \"GET /api/apm/backends/{backendName}/charts/error_rate\" | \"GET /api/apm/fallback_to_transactions\" | \"GET /api/apm/has_data\"" ], "path": "x-pack/plugins/apm/server/routes/get_global_apm_server_route_repository.ts", "deprecated": false, @@ -815,7 +819,7 @@ "label": "APMConfig", "description": [], "signature": [ - "{ 'apm_oss.transactionIndices': string; 'apm_oss.spanIndices': string; 'apm_oss.errorIndices': string; 'apm_oss.metricsIndices': string; 'apm_oss.sourcemapIndices': string; 'apm_oss.onboardingIndices': string; 'xpack.apm.serviceMapEnabled': boolean; 'xpack.apm.serviceMapFingerprintBucketSize': number; 'xpack.apm.serviceMapTraceIdBucketSize': number; 'xpack.apm.serviceMapFingerprintGlobalBucketSize': number; 'xpack.apm.serviceMapTraceIdGlobalBucketSize': number; 'xpack.apm.serviceMapMaxTracesPerRequest': number; 'xpack.apm.ui.enabled': boolean; 'xpack.apm.maxServiceEnvironments': number; 'xpack.apm.maxServiceSelection': number; 'xpack.apm.ui.maxTraceItems': number; 'xpack.apm.ui.transactionGroupBucketSize': number; 'xpack.apm.autocreateApmIndexPattern': boolean; 'xpack.apm.telemetryCollectionEnabled': boolean; 'xpack.apm.searchAggregatedTransactions': ", + "{ 'apm_oss.transactionIndices': string; 'apm_oss.spanIndices': string; 'apm_oss.errorIndices': string; 'apm_oss.metricsIndices': string; 'apm_oss.sourcemapIndices': string; 'apm_oss.onboardingIndices': string; 'xpack.apm.serviceMapEnabled': boolean; 'xpack.apm.serviceMapFingerprintBucketSize': number; 'xpack.apm.serviceMapTraceIdBucketSize': number; 'xpack.apm.serviceMapFingerprintGlobalBucketSize': number; 'xpack.apm.serviceMapTraceIdGlobalBucketSize': number; 'xpack.apm.serviceMapMaxTracesPerRequest': number; 'xpack.apm.ui.enabled': boolean; 'xpack.apm.ui.maxTraceItems': number; 'xpack.apm.ui.transactionGroupBucketSize': number; 'xpack.apm.autocreateApmIndexPattern': boolean; 'xpack.apm.telemetryCollectionEnabled': boolean; 'xpack.apm.searchAggregatedTransactions': ", "SearchAggregatedTransactionSetting", "; 'xpack.apm.metricsInterval': number; 'xpack.apm.agent.migrations.enabled': boolean; }" ], @@ -2680,6 +2684,26 @@ }, ", { serviceInfrastructure: { containerIds: string[]; hostNames: string[]; }; }, ", "APMRouteCreateOptions", + ">; } & { \"GET /internal/apm/suggestions\": ", + "ServerRoute", + "<\"GET /internal/apm/suggestions\", ", + "PartialC", + "<{ query: ", + "TypeC", + "<{ field: ", + "StringC", + "; string: ", + "StringC", + "; }>; }>, ", + { + "pluginId": "apm", + "scope": "server", + "docId": "kibApmPluginApi", + "section": "def-server.APMRouteHandlerResources", + "text": "APMRouteHandlerResources" + }, + ", { terms: string[]; }, ", + "APMRouteCreateOptions", ">; } & { \"GET /api/apm/traces/{traceId}\": ", "ServerRoute", "<\"GET /api/apm/traces/{traceId}\", ", @@ -3480,7 +3504,7 @@ "SearchHit", "<", "AgentConfiguration", - ", undefined, undefined>, ", + ", undefined, undefined> | null, ", "APMRouteCreateOptions", ">; } & { \"GET /api/apm/settings/agent-configuration/services\": ", "ServerRoute", @@ -4318,7 +4342,7 @@ "signature": [ "{ readonly enabled: boolean; readonly serviceMapEnabled: boolean; readonly serviceMapFingerprintBucketSize: number; readonly serviceMapTraceIdBucketSize: number; readonly serviceMapFingerprintGlobalBucketSize: number; readonly serviceMapTraceIdGlobalBucketSize: number; readonly serviceMapMaxTracesPerRequest: number; readonly autocreateApmIndexPattern: boolean; readonly ui: Readonly<{} & { enabled: boolean; transactionGroupBucketSize: number; maxTraceItems: number; }>; readonly searchAggregatedTransactions: ", "SearchAggregatedTransactionSetting", - "; readonly telemetryCollectionEnabled: boolean; readonly metricsInterval: number; readonly maxServiceEnvironments: number; readonly maxServiceSelection: number; readonly profilingEnabled: boolean; readonly agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; }" + "; readonly telemetryCollectionEnabled: boolean; readonly metricsInterval: number; readonly profilingEnabled: boolean; readonly agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; }" ], "path": "x-pack/plugins/apm/server/index.ts", "deprecated": false, @@ -4345,7 +4369,7 @@ "description": [], "signature": [ "Observable", - "<{ 'apm_oss.transactionIndices': string; 'apm_oss.spanIndices': string; 'apm_oss.errorIndices': string; 'apm_oss.metricsIndices': string; 'apm_oss.sourcemapIndices': string; 'apm_oss.onboardingIndices': string; 'xpack.apm.serviceMapEnabled': boolean; 'xpack.apm.serviceMapFingerprintBucketSize': number; 'xpack.apm.serviceMapTraceIdBucketSize': number; 'xpack.apm.serviceMapFingerprintGlobalBucketSize': number; 'xpack.apm.serviceMapTraceIdGlobalBucketSize': number; 'xpack.apm.serviceMapMaxTracesPerRequest': number; 'xpack.apm.ui.enabled': boolean; 'xpack.apm.maxServiceEnvironments': number; 'xpack.apm.maxServiceSelection': number; 'xpack.apm.ui.maxTraceItems': number; 'xpack.apm.ui.transactionGroupBucketSize': number; 'xpack.apm.autocreateApmIndexPattern': boolean; 'xpack.apm.telemetryCollectionEnabled': boolean; 'xpack.apm.searchAggregatedTransactions': ", + "<{ 'apm_oss.transactionIndices': string; 'apm_oss.spanIndices': string; 'apm_oss.errorIndices': string; 'apm_oss.metricsIndices': string; 'apm_oss.sourcemapIndices': string; 'apm_oss.onboardingIndices': string; 'xpack.apm.serviceMapEnabled': boolean; 'xpack.apm.serviceMapFingerprintBucketSize': number; 'xpack.apm.serviceMapTraceIdBucketSize': number; 'xpack.apm.serviceMapFingerprintGlobalBucketSize': number; 'xpack.apm.serviceMapTraceIdGlobalBucketSize': number; 'xpack.apm.serviceMapMaxTracesPerRequest': number; 'xpack.apm.ui.enabled': boolean; 'xpack.apm.ui.maxTraceItems': number; 'xpack.apm.ui.transactionGroupBucketSize': number; 'xpack.apm.autocreateApmIndexPattern': boolean; 'xpack.apm.telemetryCollectionEnabled': boolean; 'xpack.apm.searchAggregatedTransactions': ", "SearchAggregatedTransactionSetting", "; 'xpack.apm.metricsInterval': number; 'xpack.apm.agent.migrations.enabled': boolean; }>" ], @@ -4393,7 +4417,11 @@ "InferSearchResponseOf", ">, ESSearchRequestOf, {}>>; }>" + ">, ESSearchRequestOf, {}>>; termsEnum(operationName: string, params: ", + "APMEventESTermsEnumRequest", + "): Promise<", + "TermsEnumResponse", + ">; }>" ], "path": "x-pack/plugins/apm/server/types.ts", "deprecated": false, diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 1ff46ab13b246..f3f38bcdd54ae 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -18,7 +18,7 @@ Contact [APM UI](https://github.com/orgs/elastic/teams/apm-ui) for questions reg | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 42 | 0 | 42 | 36 | +| 42 | 0 | 42 | 37 | ## Client diff --git a/api_docs/core.mdx b/api_docs/core.mdx index cd4e34fa0c841..b743bff9a3f79 100644 --- a/api_docs/core.mdx +++ b/api_docs/core.mdx @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2294 | 27 | 1021 | 30 | +| 2295 | 27 | 1021 | 30 | ## Client diff --git a/api_docs/core_application.mdx b/api_docs/core_application.mdx index daf8cea5d58d1..8149d2a704c80 100644 --- a/api_docs/core_application.mdx +++ b/api_docs/core_application.mdx @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2294 | 27 | 1021 | 30 | +| 2295 | 27 | 1021 | 30 | ## Client diff --git a/api_docs/core_chrome.mdx b/api_docs/core_chrome.mdx index e1f9802303ee6..93b6f1b4b9709 100644 --- a/api_docs/core_chrome.mdx +++ b/api_docs/core_chrome.mdx @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2294 | 27 | 1021 | 30 | +| 2295 | 27 | 1021 | 30 | ## Client diff --git a/api_docs/core_http.mdx b/api_docs/core_http.mdx index 2241ea6653e66..566ad1f7e597f 100644 --- a/api_docs/core_http.mdx +++ b/api_docs/core_http.mdx @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2294 | 27 | 1021 | 30 | +| 2295 | 27 | 1021 | 30 | ## Client diff --git a/api_docs/core_saved_objects.json b/api_docs/core_saved_objects.json index 1213f1650e372..8aab160aab114 100644 --- a/api_docs/core_saved_objects.json +++ b/api_docs/core_saved_objects.json @@ -14902,6 +14902,21 @@ "path": "src/core/server/saved_objects/types.ts", "deprecated": false }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsTypeManagementDefinition.displayName", + "type": "string", + "tags": [], + "label": "displayName", + "description": [ + "\nWhen specified, will be used instead of the type's name in SO management section's labels." + ], + "signature": [ + "string | undefined" + ], + "path": "src/core/server/saved_objects/types.ts", + "deprecated": false + }, { "parentPluginId": "core", "id": "def-server.SavedObjectsTypeManagementDefinition.visibleInManagement", diff --git a/api_docs/core_saved_objects.mdx b/api_docs/core_saved_objects.mdx index ec4ef140a874c..27691a25c57e9 100644 --- a/api_docs/core_saved_objects.mdx +++ b/api_docs/core_saved_objects.mdx @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2294 | 27 | 1021 | 30 | +| 2295 | 27 | 1021 | 30 | ## Client diff --git a/api_docs/custom_integrations.json b/api_docs/custom_integrations.json index 5ec54f112b8cf..022624d625f88 100644 --- a/api_docs/custom_integrations.json +++ b/api_docs/custom_integrations.json @@ -179,7 +179,14 @@ "label": "icons", "description": [], "signature": [ - "{ src: string; type: string; }[]" + { + "pluginId": "customIntegrations", + "scope": "common", + "docId": "kibCustomIntegrationsPluginApi", + "section": "def-common.CustomIntegrationIcon", + "text": "CustomIntegrationIcon" + }, + "[]" ], "path": "src/plugins/custom_integrations/common/index.ts", "deprecated": false @@ -447,7 +454,14 @@ "label": "icons", "description": [], "signature": [ - "{ src: string; type: string; }[]" + { + "pluginId": "customIntegrations", + "scope": "common", + "docId": "kibCustomIntegrationsPluginApi", + "section": "def-common.CustomIntegrationIcon", + "text": "CustomIntegrationIcon" + }, + "[]" ], "path": "src/plugins/custom_integrations/common/index.ts", "deprecated": false @@ -477,6 +491,42 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "customIntegrations", + "id": "def-common.CustomIntegrationIcon", + "type": "Interface", + "tags": [], + "label": "CustomIntegrationIcon", + "description": [], + "path": "src/plugins/custom_integrations/common/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "customIntegrations", + "id": "def-common.CustomIntegrationIcon.src", + "type": "string", + "tags": [], + "label": "src", + "description": [], + "path": "src/plugins/custom_integrations/common/index.ts", + "deprecated": false + }, + { + "parentPluginId": "customIntegrations", + "id": "def-common.CustomIntegrationIcon.type", + "type": "CompoundType", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"eui\" | \"svg\"" + ], + "path": "src/plugins/custom_integrations/common/index.ts", + "deprecated": false + } + ], + "initialIsOpen": false } ], "enums": [], diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 5b8de40465ce5..53fcbf45e783e 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -18,7 +18,7 @@ Contact [Fleet](https://github.com/orgs/elastic/teams/fleet) for questions regar | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 66 | 0 | 66 | 0 | +| 69 | 0 | 69 | 0 | ## Client diff --git a/api_docs/data.json b/api_docs/data.json index 489397d80ad77..2f06bbbb6eab3 100644 --- a/api_docs/data.json +++ b/api_docs/data.json @@ -16126,10 +16126,6 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/common/components/navigation/helpers.ts" @@ -33469,10 +33465,6 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/common/components/navigation/helpers.ts" @@ -40231,10 +40223,6 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/common/components/navigation/helpers.ts" diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index 21e3930e5b477..dc6711d48b637 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -86,6 +86,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | canvas | - | | | canvas, regionMap, tileMap, visTypeXy | - | | | encryptedSavedObjects, actions, alerting | - | +| | actions | - | | | console | - | | | security, reporting, apm, infra, securitySolution | 7.16 | | | security, reporting, apm, infra, securitySolution | 7.16 | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 8334e7d445121..e46265dc4114d 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -18,6 +18,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [audit_logger.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/actions/server/authorization/audit_logger.ts#:~:text=LegacyAuditLogger), [audit_logger.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/actions/server/authorization/audit_logger.ts#:~:text=LegacyAuditLogger), [audit_logger.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/actions/server/authorization/audit_logger.ts#:~:text=LegacyAuditLogger) | - | | | [plugin.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/actions/server/plugin.ts#:~:text=authc) | - | | | [plugin.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/actions/server/plugin.ts#:~:text=authz) | - | +| | [plugin.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/actions/server/plugin.ts#:~:text=index), [plugin.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/actions/server/plugin.ts#:~:text=index) | - | @@ -801,14 +802,14 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [middleware.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts#:~:text=indexPatterns), [plugin.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/plugin.tsx#:~:text=indexPatterns), [dependencies_start_mock.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/mock/endpoint/dependencies_start_mock.ts#:~:text=indexPatterns) | - | | | [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx#:~:text=esFilters), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters)+ 15 more | 8.1 | | | [expandable_network.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx#:~:text=esQuery), [expandable_network.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx#:~:text=esQuery), [events_viewer.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx#:~:text=esQuery), [events_viewer.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx#:~:text=esQuery), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx#:~:text=esQuery), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx#:~:text=esQuery), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx#:~:text=esQuery), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx#:~:text=esQuery), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx#:~:text=esQuery), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx#:~:text=esQuery)+ 30 more | 8.1 | -| | [store.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/types/timeline/store.ts#:~:text=Filter), [store.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/types/timeline/store.ts#:~:text=Filter), [model.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/model.ts#:~:text=Filter), [model.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/model.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/actions.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/actions.ts#:~:text=Filter), [selectors.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/selectors.ts#:~:text=Filter), [selectors.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/selectors.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts#:~:text=Filter)+ 164 more | 8.1 | -| | [store.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/types/timeline/store.ts#:~:text=Filter), [store.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/types/timeline/store.ts#:~:text=Filter), [model.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/model.ts#:~:text=Filter), [model.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/model.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/actions.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/actions.ts#:~:text=Filter), [selectors.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/selectors.ts#:~:text=Filter), [selectors.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/selectors.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts#:~:text=Filter)+ 164 more | 8.1 | +| | [store.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/types/timeline/store.ts#:~:text=Filter), [store.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/types/timeline/store.ts#:~:text=Filter), [model.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/model.ts#:~:text=Filter), [model.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/model.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/actions.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/actions.ts#:~:text=Filter), [selectors.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/selectors.ts#:~:text=Filter), [selectors.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/selectors.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts#:~:text=Filter)+ 163 more | 8.1 | +| | [store.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/types/timeline/store.ts#:~:text=Filter), [store.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/types/timeline/store.ts#:~:text=Filter), [model.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/model.ts#:~:text=Filter), [model.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/model.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/actions.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/actions.ts#:~:text=Filter), [selectors.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/selectors.ts#:~:text=Filter), [selectors.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/selectors.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts#:~:text=Filter)+ 163 more | 8.1 | | | [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [entry_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.tsx#:~:text=IndexPattern), [entry_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.tsx#:~:text=IndexPattern), [entry_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.tsx#:~:text=IndexPattern), [list_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.tsx#:~:text=IndexPattern), [list_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.tsx#:~:text=IndexPattern)+ 10 more | - | | | [field_name_cell.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/event_details/table/field_name_cell.tsx#:~:text=IndexPatternField), [field_name_cell.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/event_details/table/field_name_cell.tsx#:~:text=IndexPatternField) | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts#:~:text=IIndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts#:~:text=IIndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts#:~:text=IIndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts#:~:text=IIndexPattern), [action.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts#:~:text=IIndexPattern), [action.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=IIndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx#:~:text=IIndexPattern)+ 76 more | - | | | [field_name_cell.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/event_details/table/field_name_cell.tsx#:~:text=IndexPatternField), [field_name_cell.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/event_details/table/field_name_cell.tsx#:~:text=IndexPatternField) | - | | | [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [entry_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.tsx#:~:text=IndexPattern), [entry_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.tsx#:~:text=IndexPattern), [entry_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.tsx#:~:text=IndexPattern), [list_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.tsx#:~:text=IndexPattern), [list_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.tsx#:~:text=IndexPattern)+ 10 more | - | -| | [store.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/types/timeline/store.ts#:~:text=Filter), [store.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/types/timeline/store.ts#:~:text=Filter), [model.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/model.ts#:~:text=Filter), [model.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/model.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/actions.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/actions.ts#:~:text=Filter), [selectors.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/selectors.ts#:~:text=Filter), [selectors.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/selectors.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts#:~:text=Filter)+ 164 more | 8.1 | +| | [store.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/types/timeline/store.ts#:~:text=Filter), [store.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/types/timeline/store.ts#:~:text=Filter), [model.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/model.ts#:~:text=Filter), [model.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/model.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/actions.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/actions.ts#:~:text=Filter), [selectors.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/selectors.ts#:~:text=Filter), [selectors.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/selectors.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts#:~:text=Filter)+ 163 more | 8.1 | | | [policy_config.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [isolation.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts#:~:text=mode), [isolation.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts#:~:text=mode)+ 2 more | - | | | [policy_config.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [isolation.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts#:~:text=mode), [isolation.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts#:~:text=mode)+ 2 more | - | | | [create_signals_migration_route.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts#:~:text=authc), [delete_signals_migration_route.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts#:~:text=authc), [finalize_signals_migration_route.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts#:~:text=authc), [common.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts#:~:text=authc) | - | diff --git a/api_docs/discover_enhanced.json b/api_docs/discover_enhanced.json index 62c4aa25e85c7..52e6ad7fda7ae 100644 --- a/api_docs/discover_enhanced.json +++ b/api_docs/discover_enhanced.json @@ -723,7 +723,7 @@ "signature": [ "{ dashboardConfig: ", "DashboardConfig", - "; loadFontAwesome: () => Promise; loadAngularBootstrap: () => Promise; config: Readonly<{} & { defaultAppId: string; }>; } | undefined" + "; loadFontAwesome: () => Promise; config: Readonly<{} & { defaultAppId: string; }>; } | undefined" ], "path": "x-pack/plugins/discover_enhanced/public/plugin.ts", "deprecated": false diff --git a/api_docs/fleet.json b/api_docs/fleet.json index 77a9fdb97afc5..8383d52d433b7 100644 --- a/api_docs/fleet.json +++ b/api_docs/fleet.json @@ -5018,7 +5018,13 @@ "description": [], "signature": [ "(options?: ", - "ListArtifactsProps", + { + "pluginId": "fleet", + "scope": "server", + "docId": "kibFleetPluginApi", + "section": "def-server.ListArtifactsProps", + "text": "ListArtifactsProps" + }, " | undefined) => Promise<", { "pluginId": "fleet", @@ -5048,7 +5054,13 @@ "label": "options", "description": [], "signature": [ - "ListArtifactsProps", + { + "pluginId": "fleet", + "scope": "server", + "docId": "kibFleetPluginApi", + "section": "def-server.ListArtifactsProps", + "text": "ListArtifactsProps" + }, " | undefined" ], "path": "x-pack/plugins/fleet/server/services/artifacts/types.ts", @@ -5454,6 +5466,20 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "fleet", + "id": "def-server.ListArtifactsProps", + "type": "Type", + "tags": [], + "label": "ListArtifactsProps", + "description": [], + "signature": [ + "Pick, \"page\" | \"perPage\" | \"sortOrder\" | \"kuery\"> & { sortField?: string | undefined; }" + ], + "path": "x-pack/plugins/fleet/server/services/artifacts/types.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "fleet", "id": "def-server.PackagePolicyServiceInterface", @@ -12304,6 +12330,15 @@ "label": "icons", "description": [], "signature": [ + "(", + { + "pluginId": "customIntegrations", + "scope": "common", + "docId": "kibCustomIntegrationsPluginApi", + "section": "def-common.CustomIntegrationIcon", + "text": "CustomIntegrationIcon" + }, + " | ", { "pluginId": "fleet", "scope": "common", @@ -12311,7 +12346,7 @@ "section": "def-common.PackageSpecIcon", "text": "PackageSpecIcon" }, - "[]" + ")[]" ], "path": "x-pack/plugins/fleet/common/types/models/epm.ts", "deprecated": false diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index c09e76db1f912..a4a9ea4b2f10d 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -18,7 +18,7 @@ Contact [Fleet](https://github.com/orgs/elastic/teams/fleet) for questions regar | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 1196 | 15 | 1095 | 11 | +| 1197 | 15 | 1096 | 10 | ## Client diff --git a/api_docs/home.json b/api_docs/home.json index 551bd35836d07..6443d8081b92d 100644 --- a/api_docs/home.json +++ b/api_docs/home.json @@ -973,17 +973,9 @@ "label": "SampleDataRegistrySetup", "description": [], "signature": [ - "{ registerSampleDataset: (specProvider: ", - { - "pluginId": "home", - "scope": "server", - "docId": "kibHomePluginApi", - "section": "def-server.SampleDatasetProvider", - "text": "SampleDatasetProvider" - }, - ") => void; getSampleDatasets: () => ", + "{ getSampleDatasets: () => ", "Writable", - "[]; defaultIndex: string; previewImagePath: string; overviewDashboard: string; appLinks: Readonly<{} & { label: string; path: string; icon: string; }>[]; dataIndices: Readonly<{} & { id: string; fields: Record; timeFields: string[]; dataPath: string; currentTimeMarker: string; preserveDayOfWeekTimeOfDay: boolean; }>[]; }>>[]; addSavedObjectsToSampleDataset: (id: string, savedObjects: ", + "[]; defaultIndex: string; previewImagePath: string; overviewDashboard: string; appLinks: Readonly<{} & { label: string; path: string; icon: string; }>[]; dataIndices: Readonly<{} & { id: string; fields: Record; timeFields: string[]; dataPath: string; currentTimeMarker: string; preserveDayOfWeekTimeOfDay: boolean; }>[]; }>>[]; addSavedObjectsToSampleDataset: (id: string, savedObjects: ", "SavedObject", "[]) => void; addAppLinksToSampleDataset: (id: string, appLinks: Readonly<{} & { label: string; path: string; icon: string; }>[]) => void; replacePanelInSampleDatasetDashboard: ({ sampleDataId, dashboardId, oldEmbeddableId, embeddableId, embeddableType, embeddableConfig, }: ", "SampleDatasetDashboardPanel", @@ -1003,7 +995,7 @@ "signature": [ "() => ", "Writable", - "[]; defaultIndex: string; previewImagePath: string; overviewDashboard: string; appLinks: Readonly<{} & { label: string; path: string; icon: string; }>[]; dataIndices: Readonly<{} & { id: string; fields: Record; timeFields: string[]; dataPath: string; currentTimeMarker: string; preserveDayOfWeekTimeOfDay: boolean; }>[]; }>>" + "[]; defaultIndex: string; previewImagePath: string; overviewDashboard: string; appLinks: Readonly<{} & { label: string; path: string; icon: string; }>[]; dataIndices: Readonly<{} & { id: string; fields: Record; timeFields: string[]; dataPath: string; currentTimeMarker: string; preserveDayOfWeekTimeOfDay: boolean; }>[]; }>>" ], "path": "src/plugins/home/server/services/sample_data/lib/sample_dataset_registry_types.ts", "deprecated": false, @@ -1302,17 +1294,9 @@ "label": "sampleData", "description": [], "signature": [ - "{ registerSampleDataset: (specProvider: ", - { - "pluginId": "home", - "scope": "server", - "docId": "kibHomePluginApi", - "section": "def-server.SampleDatasetProvider", - "text": "SampleDatasetProvider" - }, - ") => void; getSampleDatasets: () => ", + "{ getSampleDatasets: () => ", "Writable", - "[]; defaultIndex: string; previewImagePath: string; overviewDashboard: string; appLinks: Readonly<{} & { label: string; path: string; icon: string; }>[]; dataIndices: Readonly<{} & { id: string; fields: Record; timeFields: string[]; dataPath: string; currentTimeMarker: string; preserveDayOfWeekTimeOfDay: boolean; }>[]; }>>[]; addSavedObjectsToSampleDataset: (id: string, savedObjects: ", + "[]; defaultIndex: string; previewImagePath: string; overviewDashboard: string; appLinks: Readonly<{} & { label: string; path: string; icon: string; }>[]; dataIndices: Readonly<{} & { id: string; fields: Record; timeFields: string[]; dataPath: string; currentTimeMarker: string; preserveDayOfWeekTimeOfDay: boolean; }>[]; }>>[]; addSavedObjectsToSampleDataset: (id: string, savedObjects: ", "SavedObject", "[]) => void; addAppLinksToSampleDataset: (id: string, appLinks: Readonly<{} & { label: string; path: string; icon: string; }>[]) => void; replacePanelInSampleDatasetDashboard: ({ sampleDataId, dashboardId, oldEmbeddableId, embeddableId, embeddableType, embeddableConfig, }: ", "SampleDatasetDashboardPanel", diff --git a/api_docs/index_management.json b/api_docs/index_management.json index 88217360a533b..5607c50fa0bec 100644 --- a/api_docs/index_management.json +++ b/api_docs/index_management.json @@ -168,12 +168,12 @@ { "parentPluginId": "indexManagement", "id": "def-public.Index.documents", - "type": "Any", + "type": "string", "tags": [], "label": "documents", "description": [], "signature": [ - "any" + "string | undefined" ], "path": "x-pack/plugins/index_management/common/types/indices.ts", "deprecated": false @@ -201,6 +201,16 @@ "path": "x-pack/plugins/index_management/common/types/indices.ts", "deprecated": false }, + { + "parentPluginId": "indexManagement", + "id": "def-public.Index.hidden", + "type": "boolean", + "tags": [], + "label": "hidden", + "description": [], + "path": "x-pack/plugins/index_management/common/types/indices.ts", + "deprecated": false + }, { "parentPluginId": "indexManagement", "id": "def-public.Index.aliases", @@ -421,12 +431,12 @@ { "parentPluginId": "indexManagement", "id": "def-server.Index.documents", - "type": "Any", + "type": "string", "tags": [], "label": "documents", "description": [], "signature": [ - "any" + "string | undefined" ], "path": "x-pack/plugins/index_management/common/types/indices.ts", "deprecated": false @@ -454,6 +464,16 @@ "path": "x-pack/plugins/index_management/common/types/indices.ts", "deprecated": false }, + { + "parentPluginId": "indexManagement", + "id": "def-server.Index.hidden", + "type": "boolean", + "tags": [], + "label": "hidden", + "description": [], + "path": "x-pack/plugins/index_management/common/types/indices.ts", + "deprecated": false + }, { "parentPluginId": "indexManagement", "id": "def-server.Index.aliases", @@ -1474,12 +1494,12 @@ { "parentPluginId": "indexManagement", "id": "def-common.Index.documents", - "type": "Any", + "type": "string", "tags": [], "label": "documents", "description": [], "signature": [ - "any" + "string | undefined" ], "path": "x-pack/plugins/index_management/common/types/indices.ts", "deprecated": false @@ -1507,6 +1527,16 @@ "path": "x-pack/plugins/index_management/common/types/indices.ts", "deprecated": false }, + { + "parentPluginId": "indexManagement", + "id": "def-common.Index.hidden", + "type": "boolean", + "tags": [], + "label": "hidden", + "description": [], + "path": "x-pack/plugins/index_management/common/types/indices.ts", + "deprecated": false + }, { "parentPluginId": "indexManagement", "id": "def-common.Index.aliases", @@ -2286,6 +2316,20 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "indexManagement", + "id": "def-common.MAJOR_VERSION", + "type": "string", + "tags": [], + "label": "MAJOR_VERSION", + "description": [], + "signature": [ + "\"7.16.0\"" + ], + "path": "x-pack/plugins/index_management/common/constants/plugin.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "indexManagement", "id": "def-common.TemplateType", diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index ad658b528c057..61ee094a30675 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -18,7 +18,7 @@ Contact [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-ma | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 165 | 12 | 160 | 3 | +| 169 | 9 | 164 | 3 | ## Client diff --git a/api_docs/kibana_legacy.json b/api_docs/kibana_legacy.json index ee819243c9221..81ab769db0ead 100644 --- a/api_docs/kibana_legacy.json +++ b/api_docs/kibana_legacy.json @@ -67,7 +67,7 @@ }, "<{}, { dashboardConfig: ", "DashboardConfig", - "; loadFontAwesome: () => Promise; loadAngularBootstrap: () => Promise; config: Readonly<{} & { defaultAppId: string; }>; }>) => {}" + "; loadFontAwesome: () => Promise; config: Readonly<{} & { defaultAppId: string; }>; }>) => {}" ], "path": "src/plugins/kibana_legacy/public/plugin.ts", "deprecated": false, @@ -89,7 +89,7 @@ }, "<{}, { dashboardConfig: ", "DashboardConfig", - "; loadFontAwesome: () => Promise; loadAngularBootstrap: () => Promise; config: Readonly<{} & { defaultAppId: string; }>; }>" + "; loadFontAwesome: () => Promise; config: Readonly<{} & { defaultAppId: string; }>; }>" ], "path": "src/plugins/kibana_legacy/public/plugin.ts", "deprecated": false, @@ -116,7 +116,7 @@ }, ") => { dashboardConfig: ", "DashboardConfig", - "; loadFontAwesome: () => Promise; loadAngularBootstrap: () => Promise; config: Readonly<{} & { defaultAppId: string; }>; }" + "; loadFontAwesome: () => Promise; config: Readonly<{} & { defaultAppId: string; }>; }" ], "path": "src/plugins/kibana_legacy/public/plugin.ts", "deprecated": false, @@ -180,94 +180,6 @@ "returnComment": [], "initialIsOpen": false }, - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.addFatalError", - "type": "Function", - "tags": [], - "label": "addFatalError", - "description": [], - "signature": [ - "(fatalErrors: ", - { - "pluginId": "core", - "scope": "public", - "docId": "kibCorePluginApi", - "section": "def-public.FatalErrorsSetup", - "text": "FatalErrorsSetup" - }, - ", error: string | Error | ", - { - "pluginId": "kibanaLegacy", - "scope": "public", - "docId": "kibKibanaLegacyPluginApi", - "section": "def-public.AngularHttpError", - "text": "AngularHttpError" - }, - ", location: string | undefined) => void" - ], - "path": "src/plugins/kibana_legacy/public/notify/lib/add_fatal_error.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.addFatalError.$1", - "type": "Object", - "tags": [], - "label": "fatalErrors", - "description": [], - "signature": [ - { - "pluginId": "core", - "scope": "public", - "docId": "kibCorePluginApi", - "section": "def-public.FatalErrorsSetup", - "text": "FatalErrorsSetup" - } - ], - "path": "src/plugins/kibana_legacy/public/notify/lib/add_fatal_error.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.addFatalError.$2", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "string | Error | ", - { - "pluginId": "kibanaLegacy", - "scope": "public", - "docId": "kibKibanaLegacyPluginApi", - "section": "def-public.AngularHttpError", - "text": "AngularHttpError" - } - ], - "path": "src/plugins/kibana_legacy/public/notify/lib/add_fatal_error.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.addFatalError.$3", - "type": "string", - "tags": [], - "label": "location", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/kibana_legacy/public/notify/lib/add_fatal_error.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "kibanaLegacy", "id": "def-public.configureAppAngularModule", @@ -548,37 +460,6 @@ "returnComment": [], "initialIsOpen": false }, - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.formatStack", - "type": "Function", - "tags": [], - "label": "formatStack", - "description": [], - "signature": [ - "(err: Record) => any" - ], - "path": "src/plugins/kibana_legacy/public/notify/lib/format_stack.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.formatStack.$1", - "type": "Object", - "tags": [], - "label": "err", - "description": [], - "signature": [ - "Record" - ], - "path": "src/plugins/kibana_legacy/public/notify/lib/format_stack.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "kibanaLegacy", "id": "def-public.isAngularHttpError", @@ -641,67 +522,6 @@ "returnComment": [], "initialIsOpen": false }, - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.PaginateControlsDirectiveProvider", - "type": "Function", - "tags": [], - "label": "PaginateControlsDirectiveProvider", - "description": [], - "signature": [ - "() => any" - ], - "path": "src/plugins/kibana_legacy/public/paginate/paginate.d.ts", - "deprecated": false, - "children": [], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.PaginateDirectiveProvider", - "type": "Function", - "tags": [], - "label": "PaginateDirectiveProvider", - "description": [], - "signature": [ - "($parse: any, $compile: any) => any" - ], - "path": "src/plugins/kibana_legacy/public/paginate/paginate.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.PaginateDirectiveProvider.$1", - "type": "Any", - "tags": [], - "label": "$parse", - "description": [], - "signature": [ - "any" - ], - "path": "src/plugins/kibana_legacy/public/paginate/paginate.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.PaginateDirectiveProvider.$2", - "type": "Any", - "tags": [], - "label": "$compile", - "description": [], - "signature": [ - "any" - ], - "path": "src/plugins/kibana_legacy/public/paginate/paginate.d.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "kibanaLegacy", "id": "def-public.PrivateProvider", @@ -717,158 +537,6 @@ "children": [], "returnComment": [], "initialIsOpen": false - }, - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.registerListenEventListener", - "type": "Function", - "tags": [], - "label": "registerListenEventListener", - "description": [], - "signature": [ - "($rootScope: unknown) => void" - ], - "path": "src/plugins/kibana_legacy/public/utils/register_listen_event_listener.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.registerListenEventListener.$1", - "type": "Unknown", - "tags": [], - "label": "$rootScope", - "description": [], - "signature": [ - "unknown" - ], - "path": "src/plugins/kibana_legacy/public/utils/register_listen_event_listener.d.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.subscribeWithScope", - "type": "Function", - "tags": [], - "label": "subscribeWithScope", - "description": [ - "\nSubscribe to an observable at a $scope, ensuring that the digest cycle\nis run for subscriber hooks and routing errors to fatalError if not handled." - ], - "signature": [ - "($scope: angular.IScope, observable: ", - "Observable", - ", observer: ", - "NextObserver", - " | ", - "ErrorObserver", - " | ", - "CompletionObserver", - " | undefined, fatalError: FatalErrorFn | undefined) => ", - "Subscription" - ], - "path": "src/plugins/kibana_legacy/public/angular/subscribe_with_scope.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.subscribeWithScope.$1", - "type": "Object", - "tags": [], - "label": "$scope", - "description": [], - "signature": [ - "angular.IScope" - ], - "path": "src/plugins/kibana_legacy/public/angular/subscribe_with_scope.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.subscribeWithScope.$2", - "type": "Object", - "tags": [], - "label": "observable", - "description": [], - "signature": [ - "Observable", - "" - ], - "path": "src/plugins/kibana_legacy/public/angular/subscribe_with_scope.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.subscribeWithScope.$3", - "type": "CompoundType", - "tags": [], - "label": "observer", - "description": [], - "signature": [ - "NextObserver", - " | ", - "ErrorObserver", - " | ", - "CompletionObserver", - " | undefined" - ], - "path": "src/plugins/kibana_legacy/public/angular/subscribe_with_scope.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.subscribeWithScope.$4", - "type": "Function", - "tags": [], - "label": "fatalError", - "description": [], - "signature": [ - "FatalErrorFn | undefined" - ], - "path": "src/plugins/kibana_legacy/public/angular/subscribe_with_scope.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.watchMultiDecorator", - "type": "Function", - "tags": [], - "label": "watchMultiDecorator", - "description": [], - "signature": [ - "($provide: unknown) => void" - ], - "path": "src/plugins/kibana_legacy/public/angular/watch_multi.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.watchMultiDecorator.$1", - "type": "Unknown", - "tags": [], - "label": "$provide", - "description": [], - "signature": [ - "unknown" - ], - "path": "src/plugins/kibana_legacy/public/angular/watch_multi.d.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false } ], "interfaces": [ @@ -1128,20 +796,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "kibanaLegacy", - "id": "def-public.KbnAccessibleClickProvider", - "type": "CompoundType", - "tags": [], - "label": "KbnAccessibleClickProvider", - "description": [], - "signature": [ - "angular.IDirectiveFactory | (string | angular.IDirectiveFactory)[]" - ], - "path": "src/plugins/kibana_legacy/public/utils/kbn_accessible_click.d.ts", - "deprecated": false, - "initialIsOpen": false - }, { "parentPluginId": "kibanaLegacy", "id": "def-public.KibanaLegacySetup", @@ -1166,7 +820,7 @@ "signature": [ "{ dashboardConfig: ", "DashboardConfig", - "; loadFontAwesome: () => Promise; loadAngularBootstrap: () => Promise; config: Readonly<{} & { defaultAppId: string; }>; }" + "; loadFontAwesome: () => Promise; config: Readonly<{} & { defaultAppId: string; }>; }" ], "path": "src/plugins/kibana_legacy/public/plugin.ts", "deprecated": false, diff --git a/api_docs/kibana_legacy.mdx b/api_docs/kibana_legacy.mdx index 35fddcde2414b..a167ff3d05b2a 100644 --- a/api_docs/kibana_legacy.mdx +++ b/api_docs/kibana_legacy.mdx @@ -18,7 +18,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 70 | 3 | 66 | 1 | +| 50 | 1 | 47 | 1 | ## Client diff --git a/api_docs/kibana_react.json b/api_docs/kibana_react.json index 7bdea82a4de6c..5d7a7241dd956 100644 --- a/api_docs/kibana_react.json +++ b/api_docs/kibana_react.json @@ -6669,7 +6669,7 @@ "label": "eui", "description": [], "signature": [ - "{ paddingSizes: { xs: string; s: string; m: string; l: string; xl: string; }; avatarSizing: { s: { size: string; 'font-size': string; }; m: { size: string; 'font-size': string; }; l: { size: string; 'font-size': string; }; xl: { size: string; 'font-size': string; }; }; euiBadgeGroupGutterTypes: { gutterExtraSmall: string; gutterSmall: string; }; euiBreadcrumbSpacing: string; euiBreadcrumbTruncateWidth: string; euiButtonEmptyTypes: { primary: string; danger: string; disabled: string; ghost: string; text: string; success: string; warning: string; }; euiCallOutTypes: { primary: string; success: string; warning: string; danger: string; }; euiCardSpacing: string; euiCardBottomNodeHeight: string; euiCardSelectButtonBorders: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardSelectButtonBackgrounds: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCheckableCardPadding: string; euiCodeBlockPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCollapsibleNavGroupLightBackgroundColor: string; euiCollapsibleNavGroupDarkBackgroundColor: string; euiCollapsibleNavGroupDarkHighContrastColor: string; euiColorPickerValueRange0: string; euiColorPickerValueRange1: string; euiColorPickerSaturationRange0: string; euiColorPickerSaturationRange1: string; euiColorPickerIndicatorSize: string; euiColorPickerWidth: string; euiColorPaletteDisplaySizes: { sizeExtraSmall: string; sizeSmall: string; sizeMedium: string; }; euiContextMenuWidth: string; euiControlBarBackground: string; euiControlBarText: string; euiControlBarBorderColor: string; euiControlBarInitialHeight: string; euiControlBarMaxHeight: string; euiControlBarHeights: { s: string; m: string; l: string; }; euiDataGridPrefix: string; euiDataGridStyles: string; euiZDataGrid: number; euiZHeaderBelowDataGrid: number; euiDataGridColumnResizerWidth: string; euiDataGridPopoverMaxHeight: string; euiDataGridCellPaddingS: string; euiDataGridCellPaddingM: string; euiDataGridCellPaddingL: string; euiDataGridVerticalBorder: string; euiSuperDatePickerWidth: string; euiSuperDatePickerButtonWidth: string; euiDragAndDropSpacing: { s: string; m: string; l: string; }; euiExpressionColors: { subdued: string; primary: string; success: string; secondary: string; warning: string; danger: string; accent: string; }; euiFacetGutterSizes: { gutterNone: number; gutterSmall: string; gutterMedium: string; gutterLarge: string; }; gutterTypes: { gutterExtraSmall: string; gutterSmall: string; gutterMedium: string; gutterLarge: string; gutterExtraLarge: string; }; fractions: { fourths: { percentage: string; count: number; }; thirds: { percentage: string; count: number; }; halves: { percentage: string; count: number; }; single: { percentage: string; count: number; }; }; flyoutSizes: { small: { min: string; width: string; max: string; }; medium: { min: string; width: string; max: string; }; large: { min: string; width: string; max: string; }; }; euiFlyoutBorder: string; euiFlyoutPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiFilePickerTallHeight: string; euiRangeLevelColors: { primary: string; success: string; warning: string; danger: string; }; textareaResizing: { vertical: string; horizontal: string; both: string; none: string; }; euiHeaderLinksGutterSizes: { gutterXS: string; gutterS: string; gutterM: string; gutterL: string; }; ruleMargins: { marginXSmall: string; marginSmall: string; marginMedium: string; marginLarge: string; marginXLarge: string; marginXXLarge: string; }; euiIconLoadingOpacity: number; euiIconColors: { accent: string; danger: string; ghost: string; primary: string; secondary: string; success: string; subdued: string; text: string; warning: string; inherit: string; }; euiIconSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiKeyPadMenuSize: string; euiKeyPadMenuMarginSize: string; euiLinkColors: { subdued: string; primary: string; secondary: string; success: string; accent: string; warning: string; danger: string; text: string; ghost: string; }; euiListGroupItemHoverBackground: string; euiListGroupItemHoverBackgroundGhost: string; euiListGroupGutterTypes: { gutterSmall: string; gutterMedium: string; }; euiListGroupItemColorTypes: { primary: string; text: string; subdued: string; ghost: string; }; euiListGroupItemSizeTypes: { xSmall: string; small: string; medium: string; large: string; }; euiGradientStartStop: string; euiGradientMiddle: string; euiMarkdownEditorMinHeight: string; euiPopoverArrowSize: string; euiPopoverTranslateDistance: string; euiProgressSizes: { xs: string; s: string; m: string; l: string; }; euiProgressColors: { primary: string; secondary: string; success: string; warning: string; danger: string; accent: string; subdued: string; vis0: string; vis1: string; vis2: string; vis3: string; vis4: string; vis5: string; vis6: string; vis7: string; vis8: string; vis9: string; customColor: string; }; euiResizableButtonTransitionSpeed: string; euiResizableButtonSize: string; euiSelectableListItemBorder: string; euiSelectableListItemPadding: string; euiSelectableTemplateSitewideTypes: { application: { color: string; 'font-weight': number; }; deployment: { color: string; 'font-weight': number; }; article: { color: string; 'font-weight': number; }; case: { color: string; 'font-weight': number; }; platform: { color: string; 'font-weight': number; }; }; euiSideNavEmphasizedBackgroundColor: string; euiSideNavRootTextcolor: string; euiSideNavBranchTextcolor: string; euiSideNavSelectedTextcolor: string; euiSideNavDisabledTextcolor: string; spacerSizes: { xs: string; s: string; m: string; l: string; xl: string; xxl: string; }; euiStepNumberSize: string; euiStepNumberSmallSize: string; euiStepNumberMargin: string; euiStepStatusColorsToFade: { warning: string; danger: string; disabled: string; incomplete: string; }; euiSuggestItemColors: { tint0: string; tint1: string; tint2: string; tint3: string; tint4: string; tint5: string; tint6: string; tint7: string; tint8: string; tint9: string; tint10: string; }; euiTableCellContentPadding: string; euiTableCellContentPaddingCompressed: string; euiTableCellCheckboxWidth: string; euiTableActionsAreaWidth: string; euiTableHoverColor: string; euiTableSelectedColor: string; euiTableHoverSelectedColor: string; euiTableActionsBorderColor: string; euiTableHoverClickableColor: string; euiTableFocusClickableColor: string; euiTabFontSize: string; euiTabFontSizeS: string; euiTabFontSizeL: string; euiTextColors: { default: string; subdued: string; secondary: string; success: string; accent: string; warning: string; danger: string; ghost: string; inherit: string; }; euiTextConstrainedMaxWidth: string; euiToastWidth: string; euiToastTypes: { primary: string; success: string; warning: string; danger: string; }; euiTokenGrayColor: string; euiTokenTypes: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; gray: { graphic: string; behindText: string; }; }; euiTokenTypeKeys: string; euiContrastRatioText: number; euiContrastRatioGraphic: number; euiContrastRatioDisabled: number; euiDatePickerCalendarWidth: string; euiAnimSlightBounce: string; euiAnimSlightResistance: string; euiAnimSpeedExtraFast: string; euiAnimSpeedFast: string; euiAnimSpeedNormal: string; euiAnimSpeedSlow: string; euiAnimSpeedExtraSlow: string; euiBorderWidthThin: string; euiBorderWidthThick: string; euiBorderColor: string; euiBorderRadius: string; euiBorderRadiusSmall: string; euiBorderThick: string; euiBorderThin: string; euiBorderEditable: string; euiButtonHeight: string; euiButtonHeightSmall: string; euiButtonHeightXSmall: string; euiButtonColorDisabled: string; euiButtonColorDisabledText: string; euiButtonColorGhostDisabled: string; euiButtonTypes: { primary: string; accent: string; secondary: string; success: string; warning: string; danger: string; subdued: string; ghost: string; text: string; }; euiColorGhost: string; euiColorInk: string; euiColorPrimary: string; euiColorSecondary: string; euiColorAccent: string; euiColorSuccess: string; euiColorWarning: string; euiColorDanger: string; euiColorEmptyShade: string; euiColorLightestShade: string; euiColorLightShade: string; euiColorMediumShade: string; euiColorDarkShade: string; euiColorDarkestShade: string; euiColorFullShade: string; euiPageBackgroundColor: string; euiColorHighlight: string; euiTextColor: string; euiTitleColor: string; euiTextSubduedColor: string; euiColorDisabled: string; euiColorPrimaryText: string; euiColorSecondaryText: string; euiColorAccentText: string; euiColorWarningText: string; euiColorDangerText: string; euiColorDisabledText: string; euiColorSuccessText: string; euiLinkColor: string; euiPaletteColorBlind: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; }; euiPaletteColorBlindKeys: string; euiColorVis0: string; euiColorVis1: string; euiColorVis2: string; euiColorVis3: string; euiColorVis4: string; euiColorVis5: string; euiColorVis6: string; euiColorVis7: string; euiColorVis8: string; euiColorVis9: string; euiColorVis0_behindText: string; euiColorVis1_behindText: string; euiColorVis2_behindText: string; euiColorVis3_behindText: string; euiColorVis4_behindText: string; euiColorVis5_behindText: string; euiColorVis6_behindText: string; euiColorVis7_behindText: string; euiColorVis8_behindText: string; euiColorVis9_behindText: string; euiColorChartLines: string; euiColorChartBand: string; euiCodeBlockBackgroundColor: string; euiCodeBlockColor: string; euiCodeBlockSelectedBackgroundColor: string; euiCodeBlockCommentColor: string; euiCodeBlockSelectorTagColor: string; euiCodeBlockStringColor: string; euiCodeBlockTagColor: string; euiCodeBlockNameColor: string; euiCodeBlockNumberColor: string; euiCodeBlockKeywordColor: string; euiCodeBlockFunctionTitleColor: string; euiCodeBlockTypeColor: string; euiCodeBlockAttributeColor: string; euiCodeBlockSymbolColor: string; euiCodeBlockParamsColor: string; euiCodeBlockMetaColor: string; euiCodeBlockTitleColor: string; euiCodeBlockSectionColor: string; euiCodeBlockAdditionColor: string; euiCodeBlockDeletionColor: string; euiCodeBlockSelectorClassColor: string; euiCodeBlockSelectorIdColor: string; euiFormMaxWidth: string; euiFormControlHeight: string; euiFormControlCompressedHeight: string; euiFormControlPadding: string; euiFormControlCompressedPadding: string; euiFormControlBorderRadius: number; euiFormControlCompressedBorderRadius: string; euiRadioSize: string; euiCheckBoxSize: string; euiCheckboxBorderRadius: string; euiSwitchHeight: string; euiSwitchWidth: string; euiSwitchThumbSize: string; euiSwitchIconHeight: string; euiSwitchHeightCompressed: string; euiSwitchWidthCompressed: string; euiSwitchThumbSizeCompressed: string; euiSwitchHeightMini: string; euiSwitchWidthMini: string; euiSwitchThumbSizeMini: string; euiFormBackgroundColor: string; euiFormBackgroundDisabledColor: string; euiFormBackgroundReadOnlyColor: string; euiFormBorderOpaqueColor: string; euiFormBorderColor: string; euiFormBorderDisabledColor: string; euiFormCustomControlDisabledIconColor: string; euiFormCustomControlBorderColor: string; euiFormControlDisabledColor: string; euiFormControlBoxShadow: string; euiFormControlPlaceholderText: string; euiFormInputGroupLabelBackground: string; euiFormInputGroupBorder: string; euiSwitchOffColor: string; euiFormControlLayoutGroupInputHeight: string; euiFormControlLayoutGroupInputCompressedHeight: string; euiFormControlLayoutGroupInputCompressedBorderRadius: string; euiRangeTrackColor: string; euiRangeThumbRadius: string; euiRangeThumbHeight: string; euiRangeThumbWidth: string; euiRangeThumbBorderColor: string; euiRangeTrackWidth: string; euiRangeTrackHeight: string; euiRangeTrackBorderWidth: number; euiRangeTrackBorderColor: string; euiRangeTrackRadius: string; euiRangeDisabledOpacity: number; euiRangeHighlightHeight: string; euiHeaderBackgroundColor: string; euiHeaderDarkBackgroundColor: string; euiHeaderBorderColor: string; euiHeaderBreadcrumbColor: string; euiHeaderHeight: string; euiHeaderChildSize: string; euiHeaderHeightCompensation: string; euiPageDefaultMaxWidth: string; euiPageSidebarMinWidth: string; euiPanelPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiPanelBorderRadiusModifiers: { borderRadiusNone: number; borderRadiusMedium: string; }; euiPanelBackgroundColorModifiers: { transparent: string; plain: string; subdued: string; accent: string; primary: string; success: string; warning: string; danger: string; }; euiBreakpoints: { xs: number; s: string; m: string; l: string; xl: string; }; euiBreakpointKeys: string; euiShadowColor: string; euiShadowColorLarge: string; euiSize: string; euiSizeXS: string; euiSizeS: string; euiSizeM: string; euiSizeL: string; euiSizeXL: string; euiSizeXXL: string; euiButtonMinWidth: string; euiScrollBar: string; euiScrollBarCorner: string; euiScrollBarCornerThin: string; euiFocusRingColor: string; euiFocusRingAnimStartColor: string; euiFocusRingAnimStartSize: string; euiFocusRingAnimStartSizeLarge: string; euiFocusRingSizeLarge: string; euiFocusRingSize: string; euiFocusTransparency: number; euiFocusTransparencyPercent: string; euiFocusBackgroundColor: string; euiTooltipBackgroundColor: string; euiTooltipAnimations: { top: string; left: string; bottom: string; right: string; }; euiFontFamily: string; euiCodeFontFamily: string; euiFontFeatureSettings: string; euiTextScale: string; euiFontSize: string; euiFontSizeXS: string; euiFontSizeS: string; euiFontSizeM: string; euiFontSizeL: string; euiFontSizeXL: string; euiFontSizeXXL: string; euiLineHeight: number; euiBodyLineHeight: number; euiFontWeightLight: number; euiFontWeightRegular: number; euiFontWeightMedium: number; euiFontWeightSemiBold: number; euiFontWeightBold: number; euiCodeFontWeightRegular: number; euiCodeFontWeightBold: number; euiTitles: { xxxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xs: { 'font-size': string; 'line-height': string; 'font-weight': number; 'letter-spacing': string; }; s: { 'font-size': string; 'line-height': string; 'font-weight': number; 'letter-spacing': string; }; m: { 'font-size': string; 'line-height': string; 'font-weight': number; 'letter-spacing': string; }; l: { 'font-size': string; 'line-height': string; 'font-weight': number; 'letter-spacing': string; }; }; euiZLevel0: number; euiZLevel1: number; euiZLevel2: number; euiZLevel3: number; euiZLevel4: number; euiZLevel5: number; euiZLevel6: number; euiZLevel7: number; euiZLevel8: number; euiZLevel9: number; euiZToastList: number; euiZModal: number; euiZMask: number; euiZNavigation: number; euiZContentMenu: number; euiZHeader: number; euiZFlyout: number; euiZMaskBelowHeader: number; euiZContent: number; } | { paddingSizes: { xs: string; s: string; m: string; l: string; xl: string; }; avatarSizing: { s: { size: string; 'font-size': string; }; m: { size: string; 'font-size': string; }; l: { size: string; 'font-size': string; }; xl: { size: string; 'font-size': string; }; }; euiBadgeGroupGutterTypes: { gutterExtraSmall: string; gutterSmall: string; }; euiBreadcrumbSpacing: string; euiBreadcrumbTruncateWidth: string; euiButtonEmptyTypes: { primary: string; danger: string; disabled: string; ghost: string; text: string; success: string; warning: string; }; euiCallOutTypes: { primary: string; success: string; warning: string; danger: string; }; euiCardSpacing: string; euiCardBottomNodeHeight: string; euiCardSelectButtonBorders: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardSelectButtonBackgrounds: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCheckableCardPadding: string; euiCodeBlockPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCollapsibleNavGroupLightBackgroundColor: string; euiCollapsibleNavGroupDarkBackgroundColor: string; euiCollapsibleNavGroupDarkHighContrastColor: string; euiColorPickerValueRange0: string; euiColorPickerValueRange1: string; euiColorPickerSaturationRange0: string; euiColorPickerSaturationRange1: string; euiColorPickerIndicatorSize: string; euiColorPickerWidth: string; euiColorPaletteDisplaySizes: { sizeExtraSmall: string; sizeSmall: string; sizeMedium: string; }; euiContextMenuWidth: string; euiControlBarBackground: string; euiControlBarText: string; euiControlBarBorderColor: string; euiControlBarInitialHeight: string; euiControlBarMaxHeight: string; euiControlBarHeights: { s: string; m: string; l: string; }; euiDataGridPrefix: string; euiDataGridStyles: string; euiZDataGrid: number; euiZHeaderBelowDataGrid: number; euiDataGridColumnResizerWidth: string; euiDataGridPopoverMaxHeight: string; euiDataGridCellPaddingS: string; euiDataGridCellPaddingM: string; euiDataGridCellPaddingL: string; euiDataGridVerticalBorder: string; euiSuperDatePickerWidth: string; euiSuperDatePickerButtonWidth: string; euiDragAndDropSpacing: { s: string; m: string; l: string; }; euiExpressionColors: { subdued: string; primary: string; success: string; secondary: string; warning: string; danger: string; accent: string; }; euiFacetGutterSizes: { gutterNone: number; gutterSmall: string; gutterMedium: string; gutterLarge: string; }; gutterTypes: { gutterExtraSmall: string; gutterSmall: string; gutterMedium: string; gutterLarge: string; gutterExtraLarge: string; }; fractions: { fourths: { percentage: string; count: number; }; thirds: { percentage: string; count: number; }; halves: { percentage: string; count: number; }; single: { percentage: string; count: number; }; }; flyoutSizes: { small: { min: string; width: string; max: string; }; medium: { min: string; width: string; max: string; }; large: { min: string; width: string; max: string; }; }; euiFlyoutBorder: string; euiFlyoutPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiFilePickerTallHeight: string; euiRangeLevelColors: { primary: string; success: string; warning: string; danger: string; }; textareaResizing: { vertical: string; horizontal: string; both: string; none: string; }; euiHeaderLinksGutterSizes: { gutterXS: string; gutterS: string; gutterM: string; gutterL: string; }; ruleMargins: { marginXSmall: string; marginSmall: string; marginMedium: string; marginLarge: string; marginXLarge: string; marginXXLarge: string; }; euiIconLoadingOpacity: number; euiIconColors: { accent: string; danger: string; ghost: string; primary: string; secondary: string; success: string; subdued: string; text: string; warning: string; inherit: string; }; euiIconSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiKeyPadMenuSize: string; euiKeyPadMenuMarginSize: string; euiLinkColors: { subdued: string; primary: string; secondary: string; success: string; accent: string; warning: string; danger: string; text: string; ghost: string; }; euiListGroupItemHoverBackground: string; euiListGroupItemHoverBackgroundGhost: string; euiListGroupGutterTypes: { gutterSmall: string; gutterMedium: string; }; euiListGroupItemColorTypes: { primary: string; text: string; subdued: string; ghost: string; }; euiListGroupItemSizeTypes: { xSmall: string; small: string; medium: string; large: string; }; euiGradientStartStop: string; euiGradientMiddle: string; euiMarkdownEditorMinHeight: string; euiPopoverArrowSize: string; euiPopoverTranslateDistance: string; euiProgressSizes: { xs: string; s: string; m: string; l: string; }; euiProgressColors: { primary: string; secondary: string; success: string; warning: string; danger: string; accent: string; subdued: string; vis0: string; vis1: string; vis2: string; vis3: string; vis4: string; vis5: string; vis6: string; vis7: string; vis8: string; vis9: string; customColor: string; }; euiResizableButtonTransitionSpeed: string; euiResizableButtonSize: string; euiSelectableListItemBorder: string; euiSelectableListItemPadding: string; euiSelectableTemplateSitewideTypes: { application: { color: string; 'font-weight': number; }; deployment: { color: string; 'font-weight': number; }; article: { color: string; 'font-weight': number; }; case: { color: string; 'font-weight': number; }; platform: { color: string; 'font-weight': number; }; }; euiSideNavEmphasizedBackgroundColor: string; euiSideNavRootTextcolor: string; euiSideNavBranchTextcolor: string; euiSideNavSelectedTextcolor: string; euiSideNavDisabledTextcolor: string; spacerSizes: { xs: string; s: string; m: string; l: string; xl: string; xxl: string; }; euiStepNumberSize: string; euiStepNumberSmallSize: string; euiStepNumberMargin: string; euiStepStatusColorsToFade: { warning: string; danger: string; disabled: string; incomplete: string; }; euiSuggestItemColors: { tint0: string; tint1: string; tint2: string; tint3: string; tint4: string; tint5: string; tint6: string; tint7: string; tint8: string; tint9: string; tint10: string; }; euiTableCellContentPadding: string; euiTableCellContentPaddingCompressed: string; euiTableCellCheckboxWidth: string; euiTableActionsAreaWidth: string; euiTableHoverColor: string; euiTableSelectedColor: string; euiTableHoverSelectedColor: string; euiTableActionsBorderColor: string; euiTableHoverClickableColor: string; euiTableFocusClickableColor: string; euiTabFontSize: string; euiTabFontSizeS: string; euiTabFontSizeL: string; euiTextColors: { default: string; subdued: string; secondary: string; success: string; accent: string; warning: string; danger: string; ghost: string; inherit: string; }; euiTextConstrainedMaxWidth: string; euiToastWidth: string; euiToastTypes: { primary: string; success: string; warning: string; danger: string; }; euiTokenGrayColor: string; euiTokenTypes: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; gray: { graphic: string; behindText: string; }; }; euiTokenTypeKeys: string; euiContrastRatioText: number; euiContrastRatioGraphic: number; euiContrastRatioDisabled: number; euiAnimSlightBounce: string; euiAnimSlightResistance: string; euiAnimSpeedExtraFast: string; euiAnimSpeedFast: string; euiAnimSpeedNormal: string; euiAnimSpeedSlow: string; euiAnimSpeedExtraSlow: string; euiBorderWidthThin: string; euiBorderWidthThick: string; euiBorderColor: string; euiBorderRadius: string; euiBorderRadiusSmall: string; euiBorderThick: string; euiBorderThin: string; euiBorderEditable: string; euiButtonHeight: string; euiButtonHeightSmall: string; euiButtonHeightXSmall: string; euiButtonColorDisabled: string; euiButtonColorDisabledText: string; euiButtonColorGhostDisabled: string; euiButtonTypes: { primary: string; accent: string; secondary: string; success: string; warning: string; danger: string; subdued: string; ghost: string; text: string; }; euiColorGhost: string; euiColorInk: string; euiColorPrimary: string; euiColorSecondary: string; euiColorAccent: string; euiColorSuccess: string; euiColorWarning: string; euiColorDanger: string; euiColorEmptyShade: string; euiColorLightestShade: string; euiColorLightShade: string; euiColorMediumShade: string; euiColorDarkShade: string; euiColorDarkestShade: string; euiColorFullShade: string; euiPageBackgroundColor: string; euiColorHighlight: string; euiTextColor: string; euiTitleColor: string; euiTextSubduedColor: string; euiColorDisabled: string; euiColorPrimaryText: string; euiColorSecondaryText: string; euiColorAccentText: string; euiColorWarningText: string; euiColorDangerText: string; euiColorDisabledText: string; euiColorSuccessText: string; euiLinkColor: string; euiPaletteColorBlind: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; }; euiPaletteColorBlindKeys: string; euiColorVis0: string; euiColorVis1: string; euiColorVis2: string; euiColorVis3: string; euiColorVis4: string; euiColorVis5: string; euiColorVis6: string; euiColorVis7: string; euiColorVis8: string; euiColorVis9: string; euiColorVis0_behindText: string; euiColorVis1_behindText: string; euiColorVis2_behindText: string; euiColorVis3_behindText: string; euiColorVis4_behindText: string; euiColorVis5_behindText: string; euiColorVis6_behindText: string; euiColorVis7_behindText: string; euiColorVis8_behindText: string; euiColorVis9_behindText: string; euiColorChartLines: string; euiColorChartBand: string; euiCodeBlockBackgroundColor: string; euiCodeBlockColor: string; euiCodeBlockSelectedBackgroundColor: string; euiCodeBlockCommentColor: string; euiCodeBlockSelectorTagColor: string; euiCodeBlockStringColor: string; euiCodeBlockTagColor: string; euiCodeBlockNameColor: string; euiCodeBlockNumberColor: string; euiCodeBlockKeywordColor: string; euiCodeBlockFunctionTitleColor: string; euiCodeBlockTypeColor: string; euiCodeBlockAttributeColor: string; euiCodeBlockSymbolColor: string; euiCodeBlockParamsColor: string; euiCodeBlockMetaColor: string; euiCodeBlockTitleColor: string; euiCodeBlockSectionColor: string; euiCodeBlockAdditionColor: string; euiCodeBlockDeletionColor: string; euiCodeBlockSelectorClassColor: string; euiCodeBlockSelectorIdColor: string; euiFormMaxWidth: string; euiFormControlHeight: string; euiFormControlCompressedHeight: string; euiFormControlPadding: string; euiFormControlCompressedPadding: string; euiFormControlBorderRadius: string; euiFormControlCompressedBorderRadius: string; euiRadioSize: string; euiCheckBoxSize: string; euiCheckboxBorderRadius: string; euiSwitchHeight: string; euiSwitchWidth: string; euiSwitchThumbSize: string; euiSwitchIconHeight: string; euiSwitchHeightCompressed: string; euiSwitchWidthCompressed: string; euiSwitchThumbSizeCompressed: string; euiSwitchHeightMini: string; euiSwitchWidthMini: string; euiSwitchThumbSizeMini: string; euiFormBackgroundColor: string; euiFormBackgroundDisabledColor: string; euiFormBackgroundReadOnlyColor: string; euiFormBorderOpaqueColor: string; euiFormBorderColor: string; euiFormBorderDisabledColor: string; euiFormCustomControlDisabledIconColor: string; euiFormCustomControlBorderColor: string; euiFormControlDisabledColor: string; euiFormControlBoxShadow: string; euiFormControlPlaceholderText: string; euiFormInputGroupLabelBackground: string; euiFormInputGroupBorder: string; euiSwitchOffColor: string; euiFormControlLayoutGroupInputHeight: string; euiFormControlLayoutGroupInputCompressedHeight: string; euiFormControlLayoutGroupInputCompressedBorderRadius: string; euiRangeTrackColor: string; euiRangeThumbRadius: string; euiRangeThumbHeight: string; euiRangeThumbWidth: string; euiRangeThumbBorderColor: string; euiRangeTrackWidth: string; euiRangeTrackHeight: string; euiRangeTrackBorderWidth: number; euiRangeTrackBorderColor: string; euiRangeTrackRadius: string; euiRangeDisabledOpacity: number; euiRangeHighlightHeight: string; euiHeaderBackgroundColor: string; euiHeaderDarkBackgroundColor: string; euiHeaderBorderColor: string; euiHeaderBreadcrumbColor: string; euiHeaderHeight: string; euiHeaderChildSize: string; euiHeaderHeightCompensation: string; euiPageDefaultMaxWidth: string; euiPageSidebarMinWidth: string; euiPanelPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiPanelBorderRadiusModifiers: { borderRadiusNone: number; borderRadiusMedium: string; }; euiPanelBackgroundColorModifiers: { transparent: string; plain: string; subdued: string; accent: string; primary: string; success: string; warning: string; danger: string; }; euiBreakpoints: { xs: number; s: string; m: string; l: string; xl: string; }; euiBreakpointKeys: string; euiShadowColor: string; euiShadowColorLarge: string; euiSize: string; euiSizeXS: string; euiSizeS: string; euiSizeM: string; euiSizeL: string; euiSizeXL: string; euiSizeXXL: string; euiButtonMinWidth: string; euiScrollBar: string; euiScrollBarCorner: string; euiScrollBarCornerThin: string; euiFocusRingColor: string; euiFocusRingAnimStartColor: string; euiFocusRingAnimStartSize: string; euiFocusRingAnimStartSizeLarge: string; euiFocusRingSizeLarge: string; euiFocusRingSize: string; euiFocusTransparency: number; euiFocusTransparencyPercent: string; euiFocusBackgroundColor: string; euiTooltipBackgroundColor: string; euiTooltipAnimations: { top: string; left: string; bottom: string; right: string; }; euiFontFamily: string; euiCodeFontFamily: string; euiFontFeatureSettings: string; euiTextScale: string; euiFontSize: string; euiFontSizeXS: string; euiFontSizeS: string; euiFontSizeM: string; euiFontSizeL: string; euiFontSizeXL: string; euiFontSizeXXL: string; euiLineHeight: number; euiBodyLineHeight: number; euiFontWeightLight: number; euiFontWeightRegular: number; euiFontWeightMedium: number; euiFontWeightSemiBold: number; euiFontWeightBold: number; euiCodeFontWeightRegular: number; euiCodeFontWeightBold: number; euiTitles: { xxxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; s: { 'font-size': string; 'line-height': string; 'font-weight': number; }; m: { 'font-size': string; 'line-height': string; 'font-weight': number; }; l: { 'font-size': string; 'line-height': string; 'font-weight': number; }; }; euiZLevel0: number; euiZLevel1: number; euiZLevel2: number; euiZLevel3: number; euiZLevel4: number; euiZLevel5: number; euiZLevel6: number; euiZLevel7: number; euiZLevel8: number; euiZLevel9: number; euiZToastList: number; euiZModal: number; euiZMask: number; euiZNavigation: number; euiZContentMenu: number; euiZHeader: number; euiZFlyout: number; euiZMaskBelowHeader: number; euiZContent: number; euiDatePickerCalendarWidth: string; euiDatePickerPadding: string; euiDatePickerGap: string; euiDatePickerCalendarColumns: number; euiDatePickerButtonSize: string; euiDatePickerMinControlWidth: string; euiDatePickerMaxControlWidth: string; euiButtonDefaultTransparency: number; euiButtonFontWeight: number; euiRangeHighlightColor: string; euiRangeThumbBackgroundColor: string; euiRangeTrackCompressedHeight: string; euiRangeHighlightCompressedHeight: string; euiRangeHeight: string; euiRangeCompressedHeight: string; euiStepStatusColors: { default: string; complete: string; warning: string; danger: string; }; }" + "{ paddingSizes: { xs: string; s: string; m: string; l: string; xl: string; }; avatarSizing: { s: { size: string; 'font-size': string; }; m: { size: string; 'font-size': string; }; l: { size: string; 'font-size': string; }; xl: { size: string; 'font-size': string; }; }; euiBadgeGroupGutterTypes: { gutterExtraSmall: string; gutterSmall: string; }; euiBreadcrumbSpacing: string; euiBreadcrumbTruncateWidth: string; euiButtonEmptyTypes: { primary: string; danger: string; disabled: string; ghost: string; text: string; success: string; warning: string; }; euiCallOutTypes: { primary: string; success: string; warning: string; danger: string; }; euiCardSpacing: string; euiCardBottomNodeHeight: string; euiCardSelectButtonBorders: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardSelectButtonBackgrounds: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCheckableCardPadding: string; euiCodeBlockPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCollapsibleNavGroupLightBackgroundColor: string; euiCollapsibleNavGroupDarkBackgroundColor: string; euiCollapsibleNavGroupDarkHighContrastColor: string; euiColorPickerValueRange0: string; euiColorPickerValueRange1: string; euiColorPickerSaturationRange0: string; euiColorPickerSaturationRange1: string; euiColorPickerIndicatorSize: string; euiColorPickerWidth: string; euiColorPaletteDisplaySizes: { sizeExtraSmall: string; sizeSmall: string; sizeMedium: string; }; euiContextMenuWidth: string; euiControlBarBackground: string; euiControlBarText: string; euiControlBarBorderColor: string; euiControlBarInitialHeight: string; euiControlBarMaxHeight: string; euiControlBarHeights: { s: string; m: string; l: string; }; euiDataGridPrefix: string; euiDataGridStyles: string; euiZDataGrid: number; euiZHeaderBelowDataGrid: number; euiDataGridColumnResizerWidth: string; euiDataGridPopoverMaxHeight: string; euiDataGridCellPaddingS: string; euiDataGridCellPaddingM: string; euiDataGridCellPaddingL: string; euiDataGridVerticalBorder: string; euiSuperDatePickerWidth: string; euiSuperDatePickerButtonWidth: string; euiDragAndDropSpacing: { s: string; m: string; l: string; }; euiExpressionColors: { subdued: string; primary: string; success: string; secondary: string; warning: string; danger: string; accent: string; }; euiFacetGutterSizes: { gutterNone: number; gutterSmall: string; gutterMedium: string; gutterLarge: string; }; gutterTypes: { gutterExtraSmall: string; gutterSmall: string; gutterMedium: string; gutterLarge: string; gutterExtraLarge: string; }; fractions: { fourths: { percentage: string; count: number; }; thirds: { percentage: string; count: number; }; halves: { percentage: string; count: number; }; single: { percentage: string; count: number; }; }; flyoutSizes: { small: { min: string; width: string; max: string; }; medium: { min: string; width: string; max: string; }; large: { min: string; width: string; max: string; }; }; euiFlyoutBorder: string; euiFlyoutPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiFilePickerTallHeight: string; euiRangeLevelColors: { primary: string; success: string; warning: string; danger: string; }; textareaResizing: { vertical: string; horizontal: string; both: string; none: string; }; euiHeaderLinksGutterSizes: { gutterXS: string; gutterS: string; gutterM: string; gutterL: string; }; ruleMargins: { marginXSmall: string; marginSmall: string; marginMedium: string; marginLarge: string; marginXLarge: string; marginXXLarge: string; }; euiIconLoadingOpacity: number; euiIconColors: { accent: string; danger: string; ghost: string; primary: string; secondary: string; success: string; subdued: string; text: string; warning: string; inherit: string; }; euiIconSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiKeyPadMenuSize: string; euiKeyPadMenuMarginSize: string; euiLinkColors: { subdued: string; primary: string; secondary: string; success: string; accent: string; warning: string; danger: string; text: string; ghost: string; }; euiListGroupItemHoverBackground: string; euiListGroupItemHoverBackgroundGhost: string; euiListGroupGutterTypes: { gutterSmall: string; gutterMedium: string; }; euiListGroupItemColorTypes: { primary: string; text: string; subdued: string; ghost: string; }; euiListGroupItemSizeTypes: { xSmall: string; small: string; medium: string; large: string; }; euiGradientStartStop: string; euiGradientMiddle: string; euiMarkdownEditorMinHeight: string; euiPopoverArrowSize: string; euiPopoverTranslateDistance: string; euiProgressSizes: { xs: string; s: string; m: string; l: string; }; euiProgressColors: { primary: string; secondary: string; success: string; warning: string; danger: string; accent: string; subdued: string; vis0: string; vis1: string; vis2: string; vis3: string; vis4: string; vis5: string; vis6: string; vis7: string; vis8: string; vis9: string; customColor: string; }; euiResizableButtonTransitionSpeed: string; euiResizableButtonSize: string; euiSelectableListItemBorder: string; euiSelectableListItemPadding: string; euiSelectableTemplateSitewideTypes: { application: { color: string; 'font-weight': number; }; deployment: { color: string; 'font-weight': number; }; article: { color: string; 'font-weight': number; }; case: { color: string; 'font-weight': number; }; platform: { color: string; 'font-weight': number; }; }; euiSideNavEmphasizedBackgroundColor: string; euiSideNavRootTextcolor: string; euiSideNavBranchTextcolor: string; euiSideNavSelectedTextcolor: string; euiSideNavDisabledTextcolor: string; spacerSizes: { xs: string; s: string; m: string; l: string; xl: string; xxl: string; }; euiStepNumberSize: string; euiStepNumberSmallSize: string; euiStepNumberMargin: string; euiStepStatusColorsToFade: { warning: string; danger: string; disabled: string; incomplete: string; }; euiSuggestItemColors: { tint0: string; tint1: string; tint2: string; tint3: string; tint4: string; tint5: string; tint6: string; tint7: string; tint8: string; tint9: string; tint10: string; }; euiTableCellContentPadding: string; euiTableCellContentPaddingCompressed: string; euiTableCellCheckboxWidth: string; euiTableActionsAreaWidth: string; euiTableHoverColor: string; euiTableSelectedColor: string; euiTableHoverSelectedColor: string; euiTableActionsBorderColor: string; euiTableHoverClickableColor: string; euiTableFocusClickableColor: string; euiTextColors: { default: string; subdued: string; secondary: string; success: string; accent: string; warning: string; danger: string; ghost: string; inherit: string; }; euiTextConstrainedMaxWidth: string; euiToastWidth: string; euiToastTypes: { primary: string; success: string; warning: string; danger: string; }; euiTokenGrayColor: string; euiTokenTypes: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; gray: { graphic: string; behindText: string; }; }; euiTokenTypeKeys: string; euiContrastRatioText: number; euiContrastRatioGraphic: number; euiContrastRatioDisabled: number; euiDatePickerCalendarWidth: string; euiAnimSlightBounce: string; euiAnimSlightResistance: string; euiAnimSpeedExtraFast: string; euiAnimSpeedFast: string; euiAnimSpeedNormal: string; euiAnimSpeedSlow: string; euiAnimSpeedExtraSlow: string; euiBorderWidthThin: string; euiBorderWidthThick: string; euiBorderColor: string; euiBorderRadius: string; euiBorderRadiusSmall: string; euiBorderThick: string; euiBorderThin: string; euiBorderEditable: string; euiButtonHeight: string; euiButtonHeightSmall: string; euiButtonHeightXSmall: string; euiButtonColorDisabled: string; euiButtonColorDisabledText: string; euiButtonColorGhostDisabled: string; euiButtonTypes: { primary: string; accent: string; secondary: string; success: string; warning: string; danger: string; subdued: string; ghost: string; text: string; }; euiColorGhost: string; euiColorInk: string; euiColorPrimary: string; euiColorSecondary: string; euiColorAccent: string; euiColorSuccess: string; euiColorWarning: string; euiColorDanger: string; euiColorEmptyShade: string; euiColorLightestShade: string; euiColorLightShade: string; euiColorMediumShade: string; euiColorDarkShade: string; euiColorDarkestShade: string; euiColorFullShade: string; euiPageBackgroundColor: string; euiColorHighlight: string; euiTextColor: string; euiTitleColor: string; euiTextSubduedColor: string; euiColorDisabled: string; euiColorPrimaryText: string; euiColorSecondaryText: string; euiColorAccentText: string; euiColorWarningText: string; euiColorDangerText: string; euiColorDisabledText: string; euiColorSuccessText: string; euiLinkColor: string; euiPaletteColorBlind: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; }; euiPaletteColorBlindKeys: string; euiColorVis0: string; euiColorVis1: string; euiColorVis2: string; euiColorVis3: string; euiColorVis4: string; euiColorVis5: string; euiColorVis6: string; euiColorVis7: string; euiColorVis8: string; euiColorVis9: string; euiColorVis0_behindText: string; euiColorVis1_behindText: string; euiColorVis2_behindText: string; euiColorVis3_behindText: string; euiColorVis4_behindText: string; euiColorVis5_behindText: string; euiColorVis6_behindText: string; euiColorVis7_behindText: string; euiColorVis8_behindText: string; euiColorVis9_behindText: string; euiColorChartLines: string; euiColorChartBand: string; euiCodeBlockBackgroundColor: string; euiCodeBlockColor: string; euiCodeBlockSelectedBackgroundColor: string; euiCodeBlockCommentColor: string; euiCodeBlockSelectorTagColor: string; euiCodeBlockStringColor: string; euiCodeBlockTagColor: string; euiCodeBlockNameColor: string; euiCodeBlockNumberColor: string; euiCodeBlockKeywordColor: string; euiCodeBlockFunctionTitleColor: string; euiCodeBlockTypeColor: string; euiCodeBlockAttributeColor: string; euiCodeBlockSymbolColor: string; euiCodeBlockParamsColor: string; euiCodeBlockMetaColor: string; euiCodeBlockTitleColor: string; euiCodeBlockSectionColor: string; euiCodeBlockAdditionColor: string; euiCodeBlockDeletionColor: string; euiCodeBlockSelectorClassColor: string; euiCodeBlockSelectorIdColor: string; euiFontWeightLight: number; euiFontWeightRegular: number; euiFontWeightMedium: number; euiFontWeightSemiBold: number; euiFontWeightBold: number; euiCodeFontWeightRegular: number; euiCodeFontWeightBold: number; euiFormMaxWidth: string; euiFormControlHeight: string; euiFormControlCompressedHeight: string; euiFormControlPadding: string; euiFormControlCompressedPadding: string; euiFormControlBorderRadius: number; euiFormControlCompressedBorderRadius: string; euiRadioSize: string; euiCheckBoxSize: string; euiCheckboxBorderRadius: string; euiSwitchHeight: string; euiSwitchWidth: string; euiSwitchThumbSize: string; euiSwitchIconHeight: string; euiSwitchHeightCompressed: string; euiSwitchWidthCompressed: string; euiSwitchThumbSizeCompressed: string; euiSwitchHeightMini: string; euiSwitchWidthMini: string; euiSwitchThumbSizeMini: string; euiFormBackgroundColor: string; euiFormBackgroundDisabledColor: string; euiFormBackgroundReadOnlyColor: string; euiFormBorderOpaqueColor: string; euiFormBorderColor: string; euiFormBorderDisabledColor: string; euiFormCustomControlDisabledIconColor: string; euiFormCustomControlBorderColor: string; euiFormControlDisabledColor: string; euiFormControlBoxShadow: string; euiFormControlPlaceholderText: string; euiFormInputGroupLabelBackground: string; euiFormInputGroupBorder: string; euiSwitchOffColor: string; euiFormControlLayoutGroupInputHeight: string; euiFormControlLayoutGroupInputCompressedHeight: string; euiFormControlLayoutGroupInputCompressedBorderRadius: string; euiRangeTrackColor: string; euiRangeThumbRadius: string; euiRangeThumbHeight: string; euiRangeThumbWidth: string; euiRangeThumbBorderColor: string; euiRangeTrackWidth: string; euiRangeTrackHeight: string; euiRangeTrackBorderWidth: number; euiRangeTrackBorderColor: string; euiRangeTrackRadius: string; euiRangeDisabledOpacity: number; euiRangeHighlightHeight: string; euiHeaderBackgroundColor: string; euiHeaderDarkBackgroundColor: string; euiHeaderBorderColor: string; euiHeaderBreadcrumbColor: string; euiHeaderHeight: string; euiHeaderChildSize: string; euiHeaderHeightCompensation: string; euiPageDefaultMaxWidth: string; euiPageSidebarMinWidth: string; euiPanelPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiPanelBorderRadiusModifiers: { borderRadiusNone: number; borderRadiusMedium: string; }; euiPanelBackgroundColorModifiers: { transparent: string; plain: string; subdued: string; accent: string; primary: string; success: string; warning: string; danger: string; }; euiBreakpoints: { xs: number; s: string; m: string; l: string; xl: string; }; euiBreakpointKeys: string; euiShadowColor: string; euiShadowColorLarge: string; euiSize: string; euiSizeXS: string; euiSizeS: string; euiSizeM: string; euiSizeL: string; euiSizeXL: string; euiSizeXXL: string; euiButtonMinWidth: string; euiScrollBar: string; euiScrollBarCorner: string; euiScrollBarCornerThin: string; euiFocusRingColor: string; euiFocusRingAnimStartColor: string; euiFocusRingAnimStartSize: string; euiFocusRingAnimStartSizeLarge: string; euiFocusRingSizeLarge: string; euiFocusRingSize: string; euiFocusTransparency: number; euiFocusTransparencyPercent: string; euiFocusBackgroundColor: string; euiTooltipBackgroundColor: string; euiTooltipAnimations: { top: string; left: string; bottom: string; right: string; }; euiFontFamily: string; euiCodeFontFamily: string; euiFontFeatureSettings: string; euiTextScale: string; euiFontSize: string; euiFontSizeXS: string; euiFontSizeS: string; euiFontSizeM: string; euiFontSizeL: string; euiFontSizeXL: string; euiFontSizeXXL: string; euiLineHeight: number; euiBodyLineHeight: number; euiTitles: { xxxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xs: { 'font-size': string; 'line-height': string; 'font-weight': number; 'letter-spacing': string; }; s: { 'font-size': string; 'line-height': string; 'font-weight': number; 'letter-spacing': string; }; m: { 'font-size': string; 'line-height': string; 'font-weight': number; 'letter-spacing': string; }; l: { 'font-size': string; 'line-height': string; 'font-weight': number; 'letter-spacing': string; }; }; euiZLevel0: number; euiZLevel1: number; euiZLevel2: number; euiZLevel3: number; euiZLevel4: number; euiZLevel5: number; euiZLevel6: number; euiZLevel7: number; euiZLevel8: number; euiZLevel9: number; euiZToastList: number; euiZModal: number; euiZMask: number; euiZNavigation: number; euiZContentMenu: number; euiZHeader: number; euiZFlyout: number; euiZMaskBelowHeader: number; euiZContent: number; } | { paddingSizes: { xs: string; s: string; m: string; l: string; xl: string; }; avatarSizing: { s: { size: string; 'font-size': string; }; m: { size: string; 'font-size': string; }; l: { size: string; 'font-size': string; }; xl: { size: string; 'font-size': string; }; }; euiBadgeGroupGutterTypes: { gutterExtraSmall: string; gutterSmall: string; }; euiBreadcrumbSpacing: string; euiBreadcrumbTruncateWidth: string; euiButtonEmptyTypes: { primary: string; danger: string; disabled: string; ghost: string; text: string; success: string; warning: string; }; euiCallOutTypes: { primary: string; success: string; warning: string; danger: string; }; euiCardSpacing: string; euiCardBottomNodeHeight: string; euiCardSelectButtonBorders: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardSelectButtonBackgrounds: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCheckableCardPadding: string; euiCodeBlockPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCollapsibleNavGroupLightBackgroundColor: string; euiCollapsibleNavGroupDarkBackgroundColor: string; euiCollapsibleNavGroupDarkHighContrastColor: string; euiColorPickerValueRange0: string; euiColorPickerValueRange1: string; euiColorPickerSaturationRange0: string; euiColorPickerSaturationRange1: string; euiColorPickerIndicatorSize: string; euiColorPickerWidth: string; euiColorPaletteDisplaySizes: { sizeExtraSmall: string; sizeSmall: string; sizeMedium: string; }; euiContextMenuWidth: string; euiControlBarBackground: string; euiControlBarText: string; euiControlBarBorderColor: string; euiControlBarInitialHeight: string; euiControlBarMaxHeight: string; euiControlBarHeights: { s: string; m: string; l: string; }; euiDataGridPrefix: string; euiDataGridStyles: string; euiZDataGrid: number; euiZHeaderBelowDataGrid: number; euiDataGridColumnResizerWidth: string; euiDataGridPopoverMaxHeight: string; euiDataGridCellPaddingS: string; euiDataGridCellPaddingM: string; euiDataGridCellPaddingL: string; euiDataGridVerticalBorder: string; euiSuperDatePickerWidth: string; euiSuperDatePickerButtonWidth: string; euiDragAndDropSpacing: { s: string; m: string; l: string; }; euiExpressionColors: { subdued: string; primary: string; success: string; secondary: string; warning: string; danger: string; accent: string; }; euiFacetGutterSizes: { gutterNone: number; gutterSmall: string; gutterMedium: string; gutterLarge: string; }; gutterTypes: { gutterExtraSmall: string; gutterSmall: string; gutterMedium: string; gutterLarge: string; gutterExtraLarge: string; }; fractions: { fourths: { percentage: string; count: number; }; thirds: { percentage: string; count: number; }; halves: { percentage: string; count: number; }; single: { percentage: string; count: number; }; }; flyoutSizes: { small: { min: string; width: string; max: string; }; medium: { min: string; width: string; max: string; }; large: { min: string; width: string; max: string; }; }; euiFlyoutBorder: string; euiFlyoutPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiFilePickerTallHeight: string; euiRangeLevelColors: { primary: string; success: string; warning: string; danger: string; }; textareaResizing: { vertical: string; horizontal: string; both: string; none: string; }; euiHeaderLinksGutterSizes: { gutterXS: string; gutterS: string; gutterM: string; gutterL: string; }; ruleMargins: { marginXSmall: string; marginSmall: string; marginMedium: string; marginLarge: string; marginXLarge: string; marginXXLarge: string; }; euiIconLoadingOpacity: number; euiIconColors: { accent: string; danger: string; ghost: string; primary: string; secondary: string; success: string; subdued: string; text: string; warning: string; inherit: string; }; euiIconSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiKeyPadMenuSize: string; euiKeyPadMenuMarginSize: string; euiLinkColors: { subdued: string; primary: string; secondary: string; success: string; accent: string; warning: string; danger: string; text: string; ghost: string; }; euiListGroupItemHoverBackground: string; euiListGroupItemHoverBackgroundGhost: string; euiListGroupGutterTypes: { gutterSmall: string; gutterMedium: string; }; euiListGroupItemColorTypes: { primary: string; text: string; subdued: string; ghost: string; }; euiListGroupItemSizeTypes: { xSmall: string; small: string; medium: string; large: string; }; euiGradientStartStop: string; euiGradientMiddle: string; euiMarkdownEditorMinHeight: string; euiPopoverArrowSize: string; euiPopoverTranslateDistance: string; euiProgressSizes: { xs: string; s: string; m: string; l: string; }; euiProgressColors: { primary: string; secondary: string; success: string; warning: string; danger: string; accent: string; subdued: string; vis0: string; vis1: string; vis2: string; vis3: string; vis4: string; vis5: string; vis6: string; vis7: string; vis8: string; vis9: string; customColor: string; }; euiResizableButtonTransitionSpeed: string; euiResizableButtonSize: string; euiSelectableListItemBorder: string; euiSelectableListItemPadding: string; euiSelectableTemplateSitewideTypes: { application: { color: string; 'font-weight': number; }; deployment: { color: string; 'font-weight': number; }; article: { color: string; 'font-weight': number; }; case: { color: string; 'font-weight': number; }; platform: { color: string; 'font-weight': number; }; }; euiSideNavEmphasizedBackgroundColor: string; euiSideNavRootTextcolor: string; euiSideNavBranchTextcolor: string; euiSideNavSelectedTextcolor: string; euiSideNavDisabledTextcolor: string; spacerSizes: { xs: string; s: string; m: string; l: string; xl: string; xxl: string; }; euiStepNumberSize: string; euiStepNumberSmallSize: string; euiStepNumberMargin: string; euiStepStatusColorsToFade: { warning: string; danger: string; disabled: string; incomplete: string; }; euiSuggestItemColors: { tint0: string; tint1: string; tint2: string; tint3: string; tint4: string; tint5: string; tint6: string; tint7: string; tint8: string; tint9: string; tint10: string; }; euiTableCellContentPadding: string; euiTableCellContentPaddingCompressed: string; euiTableCellCheckboxWidth: string; euiTableActionsAreaWidth: string; euiTableHoverColor: string; euiTableSelectedColor: string; euiTableHoverSelectedColor: string; euiTableActionsBorderColor: string; euiTableHoverClickableColor: string; euiTableFocusClickableColor: string; euiTextColors: { default: string; subdued: string; secondary: string; success: string; accent: string; warning: string; danger: string; ghost: string; inherit: string; }; euiTextConstrainedMaxWidth: string; euiToastWidth: string; euiToastTypes: { primary: string; success: string; warning: string; danger: string; }; euiTokenGrayColor: string; euiTokenTypes: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; gray: { graphic: string; behindText: string; }; }; euiTokenTypeKeys: string; euiContrastRatioText: number; euiContrastRatioGraphic: number; euiContrastRatioDisabled: number; euiAnimSlightBounce: string; euiAnimSlightResistance: string; euiAnimSpeedExtraFast: string; euiAnimSpeedFast: string; euiAnimSpeedNormal: string; euiAnimSpeedSlow: string; euiAnimSpeedExtraSlow: string; euiBorderWidthThin: string; euiBorderWidthThick: string; euiBorderColor: string; euiBorderRadius: string; euiBorderRadiusSmall: string; euiBorderThick: string; euiBorderThin: string; euiBorderEditable: string; euiButtonHeight: string; euiButtonHeightSmall: string; euiButtonHeightXSmall: string; euiButtonColorDisabled: string; euiButtonColorDisabledText: string; euiButtonColorGhostDisabled: string; euiButtonTypes: { primary: string; accent: string; secondary: string; success: string; warning: string; danger: string; subdued: string; ghost: string; text: string; }; euiColorGhost: string; euiColorInk: string; euiColorPrimary: string; euiColorSecondary: string; euiColorAccent: string; euiColorSuccess: string; euiColorWarning: string; euiColorDanger: string; euiColorEmptyShade: string; euiColorLightestShade: string; euiColorLightShade: string; euiColorMediumShade: string; euiColorDarkShade: string; euiColorDarkestShade: string; euiColorFullShade: string; euiPageBackgroundColor: string; euiColorHighlight: string; euiTextColor: string; euiTitleColor: string; euiTextSubduedColor: string; euiColorDisabled: string; euiColorPrimaryText: string; euiColorSecondaryText: string; euiColorAccentText: string; euiColorWarningText: string; euiColorDangerText: string; euiColorDisabledText: string; euiColorSuccessText: string; euiLinkColor: string; euiPaletteColorBlind: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; }; euiPaletteColorBlindKeys: string; euiColorVis0: string; euiColorVis1: string; euiColorVis2: string; euiColorVis3: string; euiColorVis4: string; euiColorVis5: string; euiColorVis6: string; euiColorVis7: string; euiColorVis8: string; euiColorVis9: string; euiColorVis0_behindText: string; euiColorVis1_behindText: string; euiColorVis2_behindText: string; euiColorVis3_behindText: string; euiColorVis4_behindText: string; euiColorVis5_behindText: string; euiColorVis6_behindText: string; euiColorVis7_behindText: string; euiColorVis8_behindText: string; euiColorVis9_behindText: string; euiColorChartLines: string; euiColorChartBand: string; euiCodeBlockBackgroundColor: string; euiCodeBlockColor: string; euiCodeBlockSelectedBackgroundColor: string; euiCodeBlockCommentColor: string; euiCodeBlockSelectorTagColor: string; euiCodeBlockStringColor: string; euiCodeBlockTagColor: string; euiCodeBlockNameColor: string; euiCodeBlockNumberColor: string; euiCodeBlockKeywordColor: string; euiCodeBlockFunctionTitleColor: string; euiCodeBlockTypeColor: string; euiCodeBlockAttributeColor: string; euiCodeBlockSymbolColor: string; euiCodeBlockParamsColor: string; euiCodeBlockMetaColor: string; euiCodeBlockTitleColor: string; euiCodeBlockSectionColor: string; euiCodeBlockAdditionColor: string; euiCodeBlockDeletionColor: string; euiCodeBlockSelectorClassColor: string; euiCodeBlockSelectorIdColor: string; euiFontWeightLight: number; euiFontWeightRegular: number; euiFontWeightMedium: number; euiFontWeightSemiBold: number; euiFontWeightBold: number; euiCodeFontWeightRegular: number; euiCodeFontWeightBold: number; euiFormMaxWidth: string; euiFormControlHeight: string; euiFormControlCompressedHeight: string; euiFormControlPadding: string; euiFormControlCompressedPadding: string; euiFormControlBorderRadius: string; euiFormControlCompressedBorderRadius: string; euiRadioSize: string; euiCheckBoxSize: string; euiCheckboxBorderRadius: string; euiSwitchHeight: string; euiSwitchWidth: string; euiSwitchThumbSize: string; euiSwitchIconHeight: string; euiSwitchHeightCompressed: string; euiSwitchWidthCompressed: string; euiSwitchThumbSizeCompressed: string; euiSwitchHeightMini: string; euiSwitchWidthMini: string; euiSwitchThumbSizeMini: string; euiFormBackgroundColor: string; euiFormBackgroundDisabledColor: string; euiFormBackgroundReadOnlyColor: string; euiFormBorderOpaqueColor: string; euiFormBorderColor: string; euiFormBorderDisabledColor: string; euiFormCustomControlDisabledIconColor: string; euiFormCustomControlBorderColor: string; euiFormControlDisabledColor: string; euiFormControlBoxShadow: string; euiFormControlPlaceholderText: string; euiFormInputGroupLabelBackground: string; euiFormInputGroupBorder: string; euiSwitchOffColor: string; euiFormControlLayoutGroupInputHeight: string; euiFormControlLayoutGroupInputCompressedHeight: string; euiFormControlLayoutGroupInputCompressedBorderRadius: string; euiRangeTrackColor: string; euiRangeThumbRadius: string; euiRangeThumbHeight: string; euiRangeThumbWidth: string; euiRangeThumbBorderColor: string; euiRangeTrackWidth: string; euiRangeTrackHeight: string; euiRangeTrackBorderWidth: number; euiRangeTrackBorderColor: string; euiRangeTrackRadius: string; euiRangeDisabledOpacity: number; euiRangeHighlightHeight: string; euiHeaderBackgroundColor: string; euiHeaderDarkBackgroundColor: string; euiHeaderBorderColor: string; euiHeaderBreadcrumbColor: string; euiHeaderHeight: string; euiHeaderChildSize: string; euiHeaderHeightCompensation: string; euiPageDefaultMaxWidth: string; euiPageSidebarMinWidth: string; euiPanelPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiPanelBorderRadiusModifiers: { borderRadiusNone: number; borderRadiusMedium: string; }; euiPanelBackgroundColorModifiers: { transparent: string; plain: string; subdued: string; accent: string; primary: string; success: string; warning: string; danger: string; }; euiBreakpoints: { xs: number; s: string; m: string; l: string; xl: string; }; euiBreakpointKeys: string; euiShadowColor: string; euiShadowColorLarge: string; euiSize: string; euiSizeXS: string; euiSizeS: string; euiSizeM: string; euiSizeL: string; euiSizeXL: string; euiSizeXXL: string; euiButtonMinWidth: string; euiScrollBar: string; euiScrollBarCorner: string; euiScrollBarCornerThin: string; euiFocusRingColor: string; euiFocusRingAnimStartColor: string; euiFocusRingAnimStartSize: string; euiFocusRingAnimStartSizeLarge: string; euiFocusRingSizeLarge: string; euiFocusRingSize: string; euiFocusTransparency: number; euiFocusTransparencyPercent: string; euiFocusBackgroundColor: string; euiTooltipBackgroundColor: string; euiTooltipAnimations: { top: string; left: string; bottom: string; right: string; }; euiFontFamily: string; euiCodeFontFamily: string; euiFontFeatureSettings: string; euiTextScale: string; euiFontSize: string; euiFontSizeXS: string; euiFontSizeS: string; euiFontSizeM: string; euiFontSizeL: string; euiFontSizeXL: string; euiFontSizeXXL: string; euiLineHeight: number; euiBodyLineHeight: number; euiTitles: { xxxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; s: { 'font-size': string; 'line-height': string; 'font-weight': number; }; m: { 'font-size': string; 'line-height': string; 'font-weight': number; }; l: { 'font-size': string; 'line-height': string; 'font-weight': number; }; }; euiZLevel0: number; euiZLevel1: number; euiZLevel2: number; euiZLevel3: number; euiZLevel4: number; euiZLevel5: number; euiZLevel6: number; euiZLevel7: number; euiZLevel8: number; euiZLevel9: number; euiZToastList: number; euiZModal: number; euiZMask: number; euiZNavigation: number; euiZContentMenu: number; euiZHeader: number; euiZFlyout: number; euiZMaskBelowHeader: number; euiZContent: number; euiDatePickerCalendarWidth: string; euiDatePickerPadding: string; euiDatePickerGap: string; euiDatePickerCalendarColumns: number; euiDatePickerButtonSize: string; euiDatePickerMinControlWidth: string; euiDatePickerMaxControlWidth: string; euiButtonDefaultTransparency: number; euiButtonFontWeight: number; euiRangeHighlightColor: string; euiRangeThumbBackgroundColor: string; euiRangeTrackCompressedHeight: string; euiRangeHighlightCompressedHeight: string; euiRangeHeight: string; euiRangeCompressedHeight: string; euiStepStatusColors: { default: string; complete: string; warning: string; danger: string; }; }" ], "path": "src/plugins/kibana_react/common/eui_styled_components.tsx", "deprecated": false diff --git a/api_docs/lens.json b/api_docs/lens.json index 9e29dace32f3e..357847bc27aba 100644 --- a/api_docs/lens.json +++ b/api_docs/lens.json @@ -3002,7 +3002,7 @@ { "parentPluginId": "lens", "id": "def-server.PluginSetupContract.taskManager", - "type": "CompoundType", + "type": "Object", "tags": [], "label": "taskManager", "description": [], diff --git a/api_docs/observability.json b/api_docs/observability.json index da0967f11c483..7bbfa5e3d2887 100644 --- a/api_docs/observability.json +++ b/api_docs/observability.json @@ -4155,6 +4155,34 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "observability", + "id": "def-common.enableInspectEsQueries", + "type": "string", + "tags": [], + "label": "enableInspectEsQueries", + "description": [], + "signature": [ + "\"observability:enableInspectEsQueries\"" + ], + "path": "x-pack/plugins/observability/common/ui_settings_keys.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "observability", + "id": "def-common.maxSuggestions", + "type": "string", + "tags": [], + "label": "maxSuggestions", + "description": [], + "signature": [ + "\"observability:maxSuggestions\"" + ], + "path": "x-pack/plugins/observability/common/ui_settings_keys.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "observability", "id": "def-common.observabilityAppId", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 0a7cebd1795ae..1b1c626829b2c 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -18,7 +18,7 @@ Contact [Observability UI](https://github.com/orgs/elastic/teams/observability-u | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 232 | 0 | 232 | 10 | +| 234 | 0 | 234 | 10 | ## Client diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index f070cceaaa73b..15e4427068517 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -18,7 +18,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 20178 | 272 | 16278 | 604 | +| 20191 | 267 | 16280 | 603 | ## Plugin Directory @@ -27,7 +27,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Kibana Alerting](https://github.com/orgs/elastic/teams/kibana-alerting-services) | - | 125 | 0 | 125 | 8 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 23 | 0 | 22 | 1 | | | [Kibana Alerting](https://github.com/orgs/elastic/teams/kibana-alerting-services) | - | 248 | 0 | 240 | 16 | -| | [APM UI](https://github.com/orgs/elastic/teams/apm-ui) | - | 42 | 0 | 42 | 36 | +| | [APM UI](https://github.com/orgs/elastic/teams/apm-ui) | - | 42 | 0 | 42 | 37 | | | [APM UI](https://github.com/orgs/elastic/teams/apm-ui) | - | 6 | 0 | 6 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 9 | 0 | 9 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 77 | 1 | 66 | 2 | @@ -37,9 +37,9 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 22 | 0 | 22 | 0 | | code | [Kibana Operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 0 | 0 | 0 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 9 | 0 | 9 | 1 | -| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2294 | 27 | 1021 | 30 | +| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2295 | 27 | 1021 | 30 | | crossClusterReplication | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | -| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 66 | 0 | 66 | 0 | +| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 69 | 0 | 69 | 0 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 145 | 1 | 132 | 10 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 51 | 0 | 50 | 0 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 11 | 0 | 11 | 0 | @@ -66,7 +66,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 216 | 0 | 98 | 2 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Index pattern fields and ambiguous values formatters | 288 | 7 | 250 | 3 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | The file upload plugin contains components and services for uploading a file, analyzing its data, and then importing the data into an Elasticsearch index. Supported file types include CSV, TSV, newline-delimited JSON and GeoJSON. | 129 | 4 | 129 | 1 | -| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1196 | 15 | 1095 | 11 | +| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1197 | 15 | 1096 | 10 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 68 | 0 | 14 | 5 | | globalSearchBar | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | globalSearchProviders | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | @@ -74,7 +74,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | grokdebugger | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 99 | 3 | 77 | 5 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 4 | 0 | 4 | 0 | -| | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 165 | 12 | 160 | 3 | +| | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 169 | 9 | 164 | 3 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | This plugin provides the ability to create index patterns via a modal flyout from any kibana app | 13 | 1 | 8 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Reusable index pattern field editor across Kibana | 42 | 2 | 39 | 3 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Index pattern management app | 2 | 0 | 2 | 0 | @@ -82,7 +82,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | ingestPipelines | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | inputControlVis | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Input Control visualization to Kibana | 0 | 0 | 0 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 123 | 6 | 96 | 4 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 70 | 3 | 66 | 1 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 50 | 1 | 47 | 1 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 6 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 297 | 8 | 260 | 5 | | kibanaUsageCollection | [Kibana Telemtry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 0 | 0 | 0 | 0 | @@ -102,7 +102,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Stack Monitoring](https://github.com/orgs/elastic/teams/stack-monitoring-ui) | - | 10 | 0 | 10 | 2 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 31 | 0 | 31 | 2 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | -| | [Observability UI](https://github.com/orgs/elastic/teams/observability-ui) | - | 232 | 0 | 232 | 10 | +| | [Observability UI](https://github.com/orgs/elastic/teams/observability-ui) | - | 234 | 0 | 234 | 10 | | | [Security asset management](https://github.com/orgs/elastic/teams/security-asset-management) | - | 11 | 0 | 11 | 0 | | painlessLab | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Presentation Utility Plugin is a set of common, shared components and toolkits for solutions within the Presentation space, (e.g. Dashboards, Canvas). | 178 | 3 | 151 | 5 | @@ -113,7 +113,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [RAC](https://github.com/orgs/elastic/teams/rac) | - | 132 | 0 | 109 | 7 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 24 | 0 | 19 | 2 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 221 | 3 | 207 | 4 | -| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 106 | 0 | 93 | 0 | +| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 111 | 0 | 98 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 54 | 0 | 50 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 90 | 3 | 51 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 22 | 0 | 17 | 1 | @@ -125,7 +125,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 23 | 1 | 22 | 1 | | | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides the Spaces feature, which allows saved objects to be organized into meaningful categories. | 203 | 0 | 20 | 1 | | | [Kibana Alerting](https://github.com/orgs/elastic/teams/kibana-alerting-services) | - | 4 | 0 | 4 | 0 | -| | [Kibana Alerting](https://github.com/orgs/elastic/teams/kibana-alerting-services) | - | 53 | 0 | 26 | 8 | +| | [Kibana Alerting](https://github.com/orgs/elastic/teams/kibana-alerting-services) | - | 70 | 0 | 32 | 7 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 41 | 0 | 0 | 0 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 36 | 0 | 36 | 4 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 1 | 0 | 1 | 0 | diff --git a/api_docs/reporting.json b/api_docs/reporting.json index 6f71f5bbb456f..949134f73d91d 100644 --- a/api_docs/reporting.json +++ b/api_docs/reporting.json @@ -2089,16 +2089,18 @@ { "parentPluginId": "reporting", "id": "def-server.ReportingSetupDeps.taskManager", - "type": "CompoundType", + "type": "Object", "tags": [], "label": "taskManager", "description": [], "signature": [ - "{ index: string; addMiddleware: (middleware: ", - "Middleware", - ") => void; } & Pick<", - "TaskTypeDictionary", - ", \"registerTaskDefinitions\">" + { + "pluginId": "taskManager", + "scope": "server", + "docId": "kibTaskManagerPluginApi", + "section": "def-server.TaskManagerSetupContract", + "text": "TaskManagerSetupContract" + } ], "path": "x-pack/plugins/reporting/server/types.ts", "deprecated": false diff --git a/api_docs/saved_objects_management.json b/api_docs/saved_objects_management.json index e04848bf38da1..c7ce9aa1c8bdf 100644 --- a/api_docs/saved_objects_management.json +++ b/api_docs/saved_objects_management.json @@ -1587,6 +1587,62 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "savedObjectsManagement", + "id": "def-common.SavedObjectManagementTypeInfo", + "type": "Interface", + "tags": [], + "label": "SavedObjectManagementTypeInfo", + "description": [], + "path": "src/plugins/saved_objects_management/common/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "savedObjectsManagement", + "id": "def-common.SavedObjectManagementTypeInfo.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "src/plugins/saved_objects_management/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "savedObjectsManagement", + "id": "def-common.SavedObjectManagementTypeInfo.namespaceType", + "type": "CompoundType", + "tags": [], + "label": "namespaceType", + "description": [], + "signature": [ + "\"multiple\" | \"single\" | \"multiple-isolated\" | \"agnostic\"" + ], + "path": "src/plugins/saved_objects_management/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "savedObjectsManagement", + "id": "def-common.SavedObjectManagementTypeInfo.hidden", + "type": "boolean", + "tags": [], + "label": "hidden", + "description": [], + "path": "src/plugins/saved_objects_management/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "savedObjectsManagement", + "id": "def-common.SavedObjectManagementTypeInfo.displayName", + "type": "string", + "tags": [], + "label": "displayName", + "description": [], + "path": "src/plugins/saved_objects_management/common/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "savedObjectsManagement", "id": "def-common.SavedObjectMetadata", diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index d48fc7634d7de..d76d82bfcf322 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 106 | 0 | 93 | 0 | +| 111 | 0 | 98 | 0 | ## Client diff --git a/api_docs/task_manager.json b/api_docs/task_manager.json index 80f5318297581..4b7f6dc95e740 100644 --- a/api_docs/task_manager.json +++ b/api_docs/task_manager.json @@ -830,6 +830,188 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "taskManager", + "id": "def-server.TaskRegisterDefinition", + "type": "Interface", + "tags": [], + "label": "TaskRegisterDefinition", + "description": [ + "\nDefines a task which can be scheduled and run by the Kibana\ntask manager." + ], + "path": "x-pack/plugins/task_manager/server/task_type_dictionary.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "taskManager", + "id": "def-server.TaskRegisterDefinition.title", + "type": "string", + "tags": [], + "label": "title", + "description": [ + "\nA brief, human-friendly title for this task." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/task_manager/server/task_type_dictionary.ts", + "deprecated": false + }, + { + "parentPluginId": "taskManager", + "id": "def-server.TaskRegisterDefinition.timeout", + "type": "string", + "tags": [], + "label": "timeout", + "description": [ + "\nHow long, in minutes or seconds, the system should wait for the task to complete\nbefore it is considered to be timed out. (e.g. '5m', the default). If\nthe task takes longer than this, Kibana will send it a kill command and\nthe task will be re-attempted." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/task_manager/server/task_type_dictionary.ts", + "deprecated": false + }, + { + "parentPluginId": "taskManager", + "id": "def-server.TaskRegisterDefinition.description", + "type": "string", + "tags": [], + "label": "description", + "description": [ + "\nAn optional more detailed description of what this task does." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/task_manager/server/task_type_dictionary.ts", + "deprecated": false + }, + { + "parentPluginId": "taskManager", + "id": "def-server.TaskRegisterDefinition.getRetry", + "type": "Function", + "tags": [], + "label": "getRetry", + "description": [ + "\nFunction that customizes how the task should behave when the task fails. This\nfunction can return `true`, `false` or a Date. True will tell task manager\nto retry using default delay logic. False will tell task manager to stop retrying\nthis task. Date will suggest when to the task manager the task should retry.\nThis function isn't used for recurring tasks, those retry as per their configured recurring schedule." + ], + "signature": [ + "((attempts: number, error: object) => boolean | Date) | undefined" + ], + "path": "x-pack/plugins/task_manager/server/task_type_dictionary.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "taskManager", + "id": "def-server.TaskRegisterDefinition.getRetry.$1", + "type": "number", + "tags": [], + "label": "attempts", + "description": [], + "signature": [ + "number" + ], + "path": "x-pack/plugins/task_manager/server/task_type_dictionary.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "taskManager", + "id": "def-server.TaskRegisterDefinition.getRetry.$2", + "type": "Uncategorized", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "object" + ], + "path": "x-pack/plugins/task_manager/server/task_type_dictionary.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "taskManager", + "id": "def-server.TaskRegisterDefinition.createTaskRunner", + "type": "Function", + "tags": [], + "label": "createTaskRunner", + "description": [ + "\nCreates an object that has a run function which performs the task's work,\nand an optional cancel function which cancels the task." + ], + "signature": [ + "(context: ", + { + "pluginId": "taskManager", + "scope": "server", + "docId": "kibTaskManagerPluginApi", + "section": "def-server.RunContext", + "text": "RunContext" + }, + ") => ", + "CancellableTask" + ], + "path": "x-pack/plugins/task_manager/server/task_type_dictionary.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "taskManager", + "id": "def-server.TaskRegisterDefinition.createTaskRunner.$1", + "type": "Object", + "tags": [], + "label": "context", + "description": [], + "signature": [ + { + "pluginId": "taskManager", + "scope": "server", + "docId": "kibTaskManagerPluginApi", + "section": "def-server.RunContext", + "text": "RunContext" + } + ], + "path": "x-pack/plugins/task_manager/server/task.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "taskManager", + "id": "def-server.TaskRegisterDefinition.maxAttempts", + "type": "number", + "tags": [], + "label": "maxAttempts", + "description": [ + "\nUp to how many times the task should retry when it fails to run. This will\ndefault to the global variable. The default value, if not specified, is 1." + ], + "signature": [ + "number | undefined" + ], + "path": "x-pack/plugins/task_manager/server/task_type_dictionary.ts", + "deprecated": false + }, + { + "parentPluginId": "taskManager", + "id": "def-server.TaskRegisterDefinition.maxConcurrency", + "type": "number", + "tags": [], + "label": "maxConcurrency", + "description": [ + "\nThe maximum number tasks of this type that can be run concurrently per Kibana instance.\nSetting this value will force Task Manager to poll for this task type separately from other task types\nwhich can add significant load to the ES cluster, so please use this configuration only when absolutely necessary.\nThe default value, if not given, is 0." + ], + "signature": [ + "number | undefined" + ], + "path": "x-pack/plugins/task_manager/server/task_type_dictionary.ts", + "deprecated": false + } + ], + "initialIsOpen": false } ], "enums": [ @@ -862,6 +1044,30 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "taskManager", + "id": "def-server.TaskDefinitionRegistry", + "type": "Type", + "tags": [], + "label": "TaskDefinitionRegistry", + "description": [ + "\nA mapping of task type id to the task definition." + ], + "signature": [ + "{ [x: string]: ", + { + "pluginId": "taskManager", + "scope": "server", + "docId": "kibTaskManagerPluginApi", + "section": "def-server.TaskRegisterDefinition", + "text": "TaskRegisterDefinition" + }, + "; }" + ], + "path": "x-pack/plugins/task_manager/server/task_type_dictionary.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "taskManager", "id": "def-server.TaskRunCreatorFunction", @@ -912,19 +1118,118 @@ "setup": { "parentPluginId": "taskManager", "id": "def-server.TaskManagerSetupContract", - "type": "Type", + "type": "Interface", "tags": [], "label": "TaskManagerSetupContract", "description": [], - "signature": [ - "{ index: string; addMiddleware: (middleware: ", - "Middleware", - ") => void; } & Pick<", - "TaskTypeDictionary", - ", \"registerTaskDefinitions\">" - ], "path": "x-pack/plugins/task_manager/server/plugin.ts", "deprecated": false, + "children": [ + { + "parentPluginId": "taskManager", + "id": "def-server.TaskManagerSetupContract.index", + "type": "string", + "tags": [ + "deprecated" + ], + "label": "index", + "description": [], + "path": "x-pack/plugins/task_manager/server/plugin.ts", + "deprecated": true, + "references": [ + { + "plugin": "actions", + "path": "x-pack/plugins/actions/server/plugin.ts" + }, + { + "plugin": "actions", + "path": "x-pack/plugins/actions/server/plugin.ts" + } + ] + }, + { + "parentPluginId": "taskManager", + "id": "def-server.TaskManagerSetupContract.addMiddleware", + "type": "Function", + "tags": [], + "label": "addMiddleware", + "description": [], + "signature": [ + "(middleware: ", + "Middleware", + ") => void" + ], + "path": "x-pack/plugins/task_manager/server/plugin.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "taskManager", + "id": "def-server.TaskManagerSetupContract.addMiddleware.$1", + "type": "Object", + "tags": [], + "label": "middleware", + "description": [], + "signature": [ + "Middleware" + ], + "path": "x-pack/plugins/task_manager/server/plugin.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "taskManager", + "id": "def-server.TaskManagerSetupContract.registerTaskDefinitions", + "type": "Function", + "tags": [], + "label": "registerTaskDefinitions", + "description": [ + "\nMethod for allowing consumers to register task definitions into the system." + ], + "signature": [ + "(taskDefinitions: Record) => void" + ], + "path": "x-pack/plugins/task_manager/server/plugin.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "taskManager", + "id": "def-server.TaskManagerSetupContract.registerTaskDefinitions.$1", + "type": "Object", + "tags": [], + "label": "taskDefinitions", + "description": [ + "- The Kibana task definitions dictionary" + ], + "signature": [ + "Record" + ], + "path": "x-pack/plugins/task_manager/server/plugin.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], "lifecycle": "setup", "initialIsOpen": true }, diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index 4379b244c22a9..5ab7b60f8b2d0 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -18,7 +18,7 @@ Contact [Kibana Alerting](https://github.com/orgs/elastic/teams/kibana-alerting- | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 53 | 0 | 26 | 8 | +| 70 | 0 | 32 | 7 | ## Server diff --git a/api_docs/url_forwarding.json b/api_docs/url_forwarding.json index e489ee114fdb0..3b821e7a2dd52 100644 --- a/api_docs/url_forwarding.json +++ b/api_docs/url_forwarding.json @@ -91,7 +91,7 @@ }, ", { kibanaLegacy }: { kibanaLegacy: { dashboardConfig: ", "DashboardConfig", - "; loadFontAwesome: () => Promise; loadAngularBootstrap: () => Promise; config: Readonly<{} & { defaultAppId: string; }>; }; }) => { navigateToDefaultApp: ({ overwriteHash }?: { overwriteHash: boolean; }) => void; navigateToLegacyKibanaUrl: (hash: string) => { navigated: boolean; }; getForwards: () => ", + "; loadFontAwesome: () => Promise; config: Readonly<{} & { defaultAppId: string; }>; }; }) => { navigateToDefaultApp: ({ overwriteHash }?: { overwriteHash: boolean; }) => void; navigateToLegacyKibanaUrl: (hash: string) => { navigated: boolean; }; getForwards: () => ", { "pluginId": "urlForwarding", "scope": "public", @@ -144,7 +144,7 @@ "signature": [ "{ dashboardConfig: ", "DashboardConfig", - "; loadFontAwesome: () => Promise; loadAngularBootstrap: () => Promise; config: Readonly<{} & { defaultAppId: string; }>; }" + "; loadFontAwesome: () => Promise; config: Readonly<{} & { defaultAppId: string; }>; }" ], "path": "src/plugins/url_forwarding/public/plugin.ts", "deprecated": false diff --git a/x-pack/plugins/task_manager/server/index.ts b/x-pack/plugins/task_manager/server/index.ts index a9b24fdf545a1..368b5a3441778 100644 --- a/x-pack/plugins/task_manager/server/index.ts +++ b/x-pack/plugins/task_manager/server/index.ts @@ -12,15 +12,18 @@ import { configSchema, TaskManagerConfig, MAX_WORKERS_LIMIT } from './config'; export const plugin = (initContext: PluginInitializerContext) => new TaskManagerPlugin(initContext); -export { +export type { TaskInstance, ConcreteTaskInstance, EphemeralTask, TaskRunCreatorFunction, - TaskStatus, RunContext, } from './task'; +export { TaskStatus } from './task'; + +export type { TaskRegisterDefinition, TaskDefinitionRegistry } from './task_type_dictionary'; + export { asInterval } from './lib/intervals'; export { isUnrecoverableError, @@ -30,7 +33,7 @@ export { export { RunNowResult } from './task_scheduling'; export { getOldestIdleActionTask } from './queries/oldest_idle_action_task'; -export { +export type { TaskManagerPlugin as TaskManager, TaskManagerSetupContract, TaskManagerStartContract, diff --git a/x-pack/plugins/task_manager/server/plugin.ts b/x-pack/plugins/task_manager/server/plugin.ts index fb401798ea20c..4c812c82b2cae 100644 --- a/x-pack/plugins/task_manager/server/plugin.ts +++ b/x-pack/plugins/task_manager/server/plugin.ts @@ -32,13 +32,18 @@ import { EphemeralTaskLifecycle } from './ephemeral_task_lifecycle'; import { EphemeralTask } from './task'; import { registerTaskManagerUsageCollector } from './usage'; -export type TaskManagerSetupContract = { +export interface TaskManagerSetupContract { /** * @deprecated */ index: string; addMiddleware: (middleware: Middleware) => void; -} & Pick; + /** + * Method for allowing consumers to register task definitions into the system. + * @param taskDefinitions - The Kibana task definitions dictionary + */ + registerTaskDefinitions: (taskDefinitions: TaskDefinitionRegistry) => void; +} export type TaskManagerStartContract = Pick< TaskScheduling, diff --git a/x-pack/plugins/task_manager/server/task_type_dictionary.ts b/x-pack/plugins/task_manager/server/task_type_dictionary.ts index 63a0548d79d32..3bc60284efc8f 100644 --- a/x-pack/plugins/task_manager/server/task_type_dictionary.ts +++ b/x-pack/plugins/task_manager/server/task_type_dictionary.ts @@ -5,13 +5,63 @@ * 2.0. */ -import { TaskDefinition, taskDefinitionSchema } from './task'; +import { TaskDefinition, taskDefinitionSchema, TaskRunCreatorFunction } from './task'; import { Logger } from '../../../../src/core/server'; -export type TaskDefinitionRegistry = Record< - string, - Omit & Pick, 'timeout'> ->; +/** + * Defines a task which can be scheduled and run by the Kibana + * task manager. + */ +export interface TaskRegisterDefinition { + /** + * A brief, human-friendly title for this task. + */ + title?: string; + /** + * How long, in minutes or seconds, the system should wait for the task to complete + * before it is considered to be timed out. (e.g. '5m', the default). If + * the task takes longer than this, Kibana will send it a kill command and + * the task will be re-attempted. + */ + timeout?: string; + /** + * An optional more detailed description of what this task does. + */ + description?: string; + /** + * Function that customizes how the task should behave when the task fails. This + * function can return `true`, `false` or a Date. True will tell task manager + * to retry using default delay logic. False will tell task manager to stop retrying + * this task. Date will suggest when to the task manager the task should retry. + * This function isn't used for recurring tasks, those retry as per their configured recurring schedule. + */ + getRetry?: (attempts: number, error: object) => boolean | Date; + + /** + * Creates an object that has a run function which performs the task's work, + * and an optional cancel function which cancels the task. + */ + createTaskRunner: TaskRunCreatorFunction; + + /** + * Up to how many times the task should retry when it fails to run. This will + * default to the global variable. The default value, if not specified, is 1. + */ + maxAttempts?: number; + /** + * The maximum number tasks of this type that can be run concurrently per Kibana instance. + * Setting this value will force Task Manager to poll for this task type separately from other task types + * which can add significant load to the ES cluster, so please use this configuration only when absolutely necessary. + * The default value, if not given, is 0. + */ + maxConcurrency?: number; +} + +/** + * A mapping of task type id to the task definition. + */ +export type TaskDefinitionRegistry = Record; + export class TaskTypeDictionary { private definitions = new Map(); private logger: Logger; From ea7ce5cad8f97b252aea4cabcde5ac6331554e80 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Thu, 30 Sep 2021 12:24:19 -0500 Subject: [PATCH 14/51] skipy flaky test. #112749 --- .../apps/triggers_actions_ui/alert_create_flyout.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts index adad80874dbc9..fa144bd5bf9f6 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts @@ -103,7 +103,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.click('test.always-firing-SelectOption'); } - describe('create alert', function () { + // FLAKY https://github.com/elastic/kibana/issues/112749 + describe.skip('create alert', function () { before(async () => { await pageObjects.common.navigateToApp('triggersActions'); await testSubjects.click('rulesTab'); From 4355ba52a833018b794bc5ae0e0d3360c6cdf3a6 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 13:45:51 -0400 Subject: [PATCH 15/51] [Security solution][Endpoint] API error when edit/create TA by policy and license under platinum (#113363) (#113545) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * New function to check if the effect scope has been changed without right license * Checks if license is under platinum when creating/updating trusted apps by policy * Change error type. Use translations in frontend for api error. Also use helper mapping function to avoid use string replace method * Remove name from constructor and changed mock function name Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: David Sánchez --- .../components/create_trusted_app_flyout.tsx | 12 ++ .../server/endpoint/errors.ts | 5 + .../endpoint/routes/trusted_apps/errors.ts | 1 - .../routes/trusted_apps/handlers.test.ts | 87 ++++++++++++- .../endpoint/routes/trusted_apps/handlers.ts | 12 +- .../endpoint/routes/trusted_apps/mocks.ts | 27 ++++ .../routes/trusted_apps/service.test.ts | 115 ++++++++++++++---- .../endpoint/routes/trusted_apps/service.ts | 46 ++++++- 8 files changed, 268 insertions(+), 37 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_flyout.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_flyout.tsx index 049ab5884b179..7abf5d77dd5e9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_flyout.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_flyout.tsx @@ -88,6 +88,18 @@ export const CreateTrustedAppFlyout = memo( policies.options.forEach((policy) => { errorMessage = errorMessage?.replace(policy.id, policy.name); }); + } else if ( + creationErrors && + creationErrors.attributes && + creationErrors.attributes.type === 'EndpointLicenseError' + ) { + errorMessage = i18n.translate( + 'xpack.securitySolution.trustedapps.createTrustedAppFlyout.byPolicyLicenseError', + { + defaultMessage: + 'Your Kibana license has been downgraded. As such, individual policy configuration is no longer supported.', + } + ); } return errorMessage; }, [creationErrors, policies]); diff --git a/x-pack/plugins/security_solution/server/endpoint/errors.ts b/x-pack/plugins/security_solution/server/endpoint/errors.ts index 6bd664401b449..fae15984d9c44 100644 --- a/x-pack/plugins/security_solution/server/endpoint/errors.ts +++ b/x-pack/plugins/security_solution/server/endpoint/errors.ts @@ -22,3 +22,8 @@ export class EndpointAppContentServicesNotStartedError extends EndpointError { super('EndpointAppContextService has not been started (EndpointAppContextService.start())'); } } +export class EndpointLicenseError extends EndpointError { + constructor() { + super('Your license level does not allow for this action.'); + } +} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/errors.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/errors.ts index 3d03040dd2605..277b19c46178b 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/errors.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/errors.ts @@ -25,7 +25,6 @@ export class TrustedAppPolicyNotExistsError extends Error { ); } } - export class TrustedAppVersionConflictError extends Error { constructor(id: string, public sourceError: Error) { super(`Trusted Application (${id}) has been updated since last retrieved`); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.test.ts index 81f1d45471594..547c1f6a2e5ff 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.test.ts @@ -6,6 +6,7 @@ */ import { KibanaResponseFactory } from 'kibana/server'; +import { Subject } from 'rxjs'; import { xpackMocks } from '../../../fixtures'; import { loggingSystemMock, httpServerMock } from '../../../../../../../src/core/server/mocks'; @@ -20,6 +21,9 @@ import { OperatingSystem, TrustedApp, } from '../../../../common/endpoint/types'; +import { LicenseService } from '../../../../common/license'; +import { ILicense } from '../../../../../licensing/common/types'; +import { licenseMock } from '../../../../../licensing/common/licensing.mock'; import { parseExperimentalConfigValue } from '../../../../common/experimental_features'; import { EndpointAppContextService } from '../../endpoint_app_context_services'; import { createConditionEntry, createEntryMatch } from './mapping'; @@ -41,7 +45,12 @@ import { updateExceptionListItemImplementationMock } from './test_utils'; import { Logger } from '@kbn/logging'; import { PackagePolicyServiceInterface } from '../../../../../fleet/server'; import { createPackagePolicyServiceMock } from '../../../../../fleet/server/mocks'; -import { getPackagePoliciesResponse, getTrustedAppByPolicy } from './mocks'; +import { + getPackagePoliciesResponse, + getPutTrustedAppByPolicyMock, + getTrustedAppByPolicy, +} from './mocks'; +import { EndpointLicenseError } from '../../errors'; const EXCEPTION_LIST_ITEM: ExceptionListItemSchema = { _version: 'abc123', @@ -95,6 +104,9 @@ const TRUSTED_APP: TrustedApp = { ], }; +const Platinum = licenseMock.createLicense({ license: { type: 'platinum', mode: 'platinum' } }); +const Gold = licenseMock.createLicense({ license: { type: 'gold', mode: 'gold' } }); + const packagePolicyClient = createPackagePolicyServiceMock() as jest.Mocked; @@ -102,6 +114,9 @@ describe('handlers', () => { beforeEach(() => { packagePolicyClient.getByIDs.mockReset(); }); + const licenseEmitter: Subject = new Subject(); + const licenseService = new LicenseService(); + licenseService.start(licenseEmitter); const createAppContextMock = () => { const context = { @@ -112,6 +127,7 @@ describe('handlers', () => { }; context.service.getPackagePolicyService = () => packagePolicyClient; + context.service.getLicenseService = () => licenseService; // Ensure that `logFactory.get()` always returns the same instance for the same given prefix const instances = new Map>(); @@ -151,6 +167,7 @@ describe('handlers', () => { beforeEach(() => { appContextMock = createAppContextMock(); exceptionsListClient = listMock.getExceptionListClient() as jest.Mocked; + licenseEmitter.next(Platinum); }); describe('getTrustedAppsDeleteRouteHandler', () => { @@ -261,6 +278,27 @@ describe('handlers', () => { body: { message: error.message, attributes: { type: error.type } }, }); }); + + it('should return error when license under platinum and by policy', async () => { + licenseEmitter.next(Gold); + const mockResponse = httpServerMock.createResponseFactory(); + packagePolicyClient.getByIDs.mockReset(); + packagePolicyClient.getByIDs.mockResolvedValueOnce(getPackagePoliciesResponse()); + + const trustedAppByPolicy = getTrustedAppByPolicy(); + await createTrustedAppHandler( + createHandlerContextMock(), + httpServerMock.createKibanaRequest({ body: trustedAppByPolicy }), + mockResponse + ); + + const error = new EndpointLicenseError(); + + expect(appContextMock.logFactory.get('trusted_apps').error).toHaveBeenCalledWith(error); + expect(mockResponse.badRequest).toHaveBeenCalledWith({ + body: { message: error.message, attributes: { type: error.name } }, + }); + }); }); describe('getTrustedAppsListRouteHandler', () => { @@ -578,18 +616,57 @@ describe('handlers', () => { packagePolicyClient.getByIDs.mockReset(); packagePolicyClient.getByIDs.mockResolvedValueOnce(getPackagePoliciesResponse()); - const trustedAppByPolicy = getTrustedAppByPolicy(); + const exceptionByPolicy = getPutTrustedAppByPolicyMock(); + const customExceptionListClient = { + ...exceptionsListClient, + getExceptionListItem: () => exceptionByPolicy, + }; + const handlerContextMock = { + ...xpackMocks.createRequestHandlerContext(), + lists: { + getListClient: jest.fn(), + getExceptionListClient: jest.fn().mockReturnValue(customExceptionListClient), + }, + } as unknown as jest.Mocked; await updateHandler( - createHandlerContextMock(), - httpServerMock.createKibanaRequest({ body: trustedAppByPolicy }), + handlerContextMock, + httpServerMock.createKibanaRequest({ body: getTrustedAppByPolicy() }), mockResponse ); expect(appContextMock.logFactory.get('trusted_apps').error).toHaveBeenCalledWith( - new TrustedAppPolicyNotExistsError(trustedAppByPolicy.name, [ + new TrustedAppPolicyNotExistsError(exceptionByPolicy.name, [ '9da95be9-9bee-4761-a8c4-28d6d9bd8c71', ]) ); }); + + it('should return error when license under platinum and by policy', async () => { + licenseEmitter.next(Gold); + packagePolicyClient.getByIDs.mockReset(); + packagePolicyClient.getByIDs.mockResolvedValueOnce(getPackagePoliciesResponse()); + + const exceptionByPolicy = getPutTrustedAppByPolicyMock(); + const customExceptionListClient = { + ...exceptionsListClient, + getExceptionListItem: () => exceptionByPolicy, + }; + const handlerContextMock = { + ...xpackMocks.createRequestHandlerContext(), + lists: { + getListClient: jest.fn(), + getExceptionListClient: jest.fn().mockReturnValue(customExceptionListClient), + }, + } as unknown as jest.Mocked; + await updateHandler( + handlerContextMock, + httpServerMock.createKibanaRequest({ body: getTrustedAppByPolicy() }), + mockResponse + ); + + expect(appContextMock.logFactory.get('trusted_apps').error).toHaveBeenCalledWith( + new EndpointLicenseError() + ); + }); }); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.ts index 13282bfacd5b1..b02b9d5430cad 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.ts @@ -35,6 +35,7 @@ import { TrustedAppPolicyNotExistsError, } from './errors'; import { PackagePolicyServiceInterface } from '../../../../../fleet/server'; +import { EndpointLicenseError } from '../../errors'; const getBodyAfterFeatureFlagCheck = ( body: PutTrustedAppUpdateRequest | PostTrustedAppCreateRequest, @@ -87,6 +88,11 @@ const errorHandler = ( return res.badRequest({ body: { message: error.message, attributes: { type: error.type } } }); } + if (error instanceof EndpointLicenseError) { + logger.error(error); + return res.badRequest({ body: { message: error.message, attributes: { type: error.name } } }); + } + if (error instanceof TrustedAppVersionConflictError) { logger.error(error); return res.conflict({ body: error }); @@ -177,7 +183,8 @@ export const getTrustedAppsCreateRouteHandler = ( exceptionListClientFromContext(context), context.core.savedObjects.client, packagePolicyClientFromEndpointContext(endpointAppContext), - body + body, + endpointAppContext.service.getLicenseService().isAtLeast('platinum') ), }); } catch (error) { @@ -206,7 +213,8 @@ export const getTrustedAppsUpdateRouteHandler = ( context.core.savedObjects.client, packagePolicyClientFromEndpointContext(endpointAppContext), req.params.id, - body + body, + endpointAppContext.service.getLicenseService().isAtLeast('platinum') ), }); } catch (error) { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/mocks.ts index e66c07f2e1627..083263809d309 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/mocks.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { PackagePolicy } from '../../../../../fleet/common'; import { @@ -36,6 +37,32 @@ export const getTrustedAppByPolicy = function (): TrustedApp { }; }; +export const getPutTrustedAppByPolicyMock = function (): ExceptionListItemSchema { + return { + id: '123', + _version: '1', + comments: [], + namespace_type: 'agnostic', + created_at: '11/11/2011T11:11:11.111', + created_by: 'admin', + updated_at: '11/11/2011T11:11:11.111', + updated_by: 'admin', + name: 'linux trusted app 1', + description: 'Linux trusted app 1', + os_types: [OperatingSystem.LINUX], + tags: ['policy:9da95be9-9bee-4761-a8c4-28d6d9bd8c71'], + entries: [ + createConditionEntry(ConditionEntryField.HASH, 'match', '1234234659af249ddf3e40864e9fb241'), + createConditionEntry(ConditionEntryField.PATH, 'match', '/bin/malware'), + ], + item_id: '1', + list_id: '1', + meta: undefined, + tie_breaker_id: '1', + type: 'simple', + }; +}; + export const getPackagePoliciesResponse = function (): PackagePolicy[] { return [ // Next line is ts-ignored as this is the response when the policy doesn't exists but the type is complaining about it. diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.test.ts index fd7baf80983a4..dce84df735929 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.test.ts @@ -36,6 +36,7 @@ import { toUpdateTrustedApp } from '../../../../common/endpoint/service/trusted_ import { updateExceptionListItemImplementationMock } from './test_utils'; import { ENDPOINT_TRUSTED_APPS_LIST_ID } from '@kbn/securitysolution-list-constants'; import { getPackagePoliciesResponse, getTrustedAppByPolicy } from './mocks'; +import { EndpointLicenseError } from '../../errors'; const exceptionsListClient = listMock.getExceptionListClient() as jest.Mocked; const packagePolicyClient = @@ -135,7 +136,8 @@ describe('service', () => { '1234234659af249ddf3e40864e9fb241' ), ], - } + }, + true ); expect(result).toEqual({ data: TRUSTED_APP }); @@ -163,7 +165,8 @@ describe('service', () => { '1234234659af249ddf3e40864e9fb241' ), ], - } + }, + true ); expect(result).toEqual({ data: TRUSTED_APP }); @@ -175,28 +178,67 @@ describe('service', () => { packagePolicyClient.getByIDs.mockReset(); packagePolicyClient.getByIDs.mockResolvedValueOnce(getPackagePoliciesResponse()); await expect( - createTrustedApp(exceptionsListClient, savedObjectClient, packagePolicyClient, { - name: 'linux trusted app 1', - description: 'Linux trusted app 1', - effectScope: { - type: 'policy', - policies: [ - 'e5cbb9cf-98aa-4303-a04b-6a1165915079', - '9da95be9-9bee-4761-a8c4-28d6d9bd8c71', + createTrustedApp( + exceptionsListClient, + savedObjectClient, + packagePolicyClient, + { + name: 'linux trusted app 1', + description: 'Linux trusted app 1', + effectScope: { + type: 'policy', + policies: [ + 'e5cbb9cf-98aa-4303-a04b-6a1165915079', + '9da95be9-9bee-4761-a8c4-28d6d9bd8c71', + ], + }, + os: OperatingSystem.LINUX, + entries: [ + createConditionEntry(ConditionEntryField.PATH, 'wildcard', '/bin/malware'), + createConditionEntry( + ConditionEntryField.HASH, + 'wildcard', + '1234234659af249ddf3e40864e9fb241' + ), ], }, - os: OperatingSystem.LINUX, - entries: [ - createConditionEntry(ConditionEntryField.PATH, 'wildcard', '/bin/malware'), - createConditionEntry( - ConditionEntryField.HASH, - 'wildcard', - '1234234659af249ddf3e40864e9fb241' - ), - ], - }) + true + ) ).rejects.toBeInstanceOf(TrustedAppPolicyNotExistsError); }); + + it('should throw when license under platinum and by policy', async () => { + packagePolicyClient.getByIDs.mockReset(); + packagePolicyClient.getByIDs.mockResolvedValueOnce(getPackagePoliciesResponse()); + await expect( + createTrustedApp( + exceptionsListClient, + savedObjectClient, + packagePolicyClient, + { + name: 'linux trusted app 1', + description: 'Linux trusted app 1', + effectScope: { + type: 'policy', + policies: [ + 'e5cbb9cf-98aa-4303-a04b-6a1165915079', + '9da95be9-9bee-4761-a8c4-28d6d9bd8c71', + ], + }, + os: OperatingSystem.LINUX, + entries: [ + createConditionEntry(ConditionEntryField.PATH, 'wildcard', '/bin/malware'), + createConditionEntry( + ConditionEntryField.HASH, + 'wildcard', + '1234234659af249ddf3e40864e9fb241' + ), + ], + }, + false + ) + ).rejects.toBeInstanceOf(EndpointLicenseError); + }); }); describe('getTrustedAppsList', () => { @@ -321,7 +363,8 @@ describe('service', () => { savedObjectClient, packagePolicyClient, TRUSTED_APP.id, - trustedAppForUpdate + trustedAppForUpdate, + true ) ).resolves.toEqual({ data: { @@ -357,7 +400,8 @@ describe('service', () => { savedObjectClient, packagePolicyClient, TRUSTED_APP.id, - toUpdateTrustedApp(TRUSTED_APP) + toUpdateTrustedApp(TRUSTED_APP), + true ) ).rejects.toBeInstanceOf(TrustedAppNotFoundError); }); @@ -374,7 +418,8 @@ describe('service', () => { savedObjectClient, packagePolicyClient, TRUSTED_APP.id, - toUpdateTrustedApp(TRUSTED_APP) + toUpdateTrustedApp(TRUSTED_APP), + true ) ).rejects.toBeInstanceOf(TrustedAppVersionConflictError); }); @@ -393,12 +438,13 @@ describe('service', () => { savedObjectClient, packagePolicyClient, TRUSTED_APP.id, - toUpdateTrustedApp(TRUSTED_APP) + toUpdateTrustedApp(TRUSTED_APP), + true ) ).rejects.toBeInstanceOf(TrustedAppNotFoundError); }); - it("should throw wrong policy error if some policy doesn't exists", async () => { + it("should throw wrong policy error if some policy doesn't exists during update", async () => { packagePolicyClient.getByIDs.mockReset(); packagePolicyClient.getByIDs.mockResolvedValueOnce(getPackagePoliciesResponse()); const trustedAppByPolicy = getTrustedAppByPolicy(); @@ -408,10 +454,27 @@ describe('service', () => { savedObjectClient, packagePolicyClient, trustedAppByPolicy.id, - toUpdateTrustedApp(trustedAppByPolicy as MaybeImmutable) + toUpdateTrustedApp(trustedAppByPolicy as MaybeImmutable), + true ) ).rejects.toBeInstanceOf(TrustedAppPolicyNotExistsError); }); + + it('should throw when license under platinum and by policy', async () => { + packagePolicyClient.getByIDs.mockReset(); + packagePolicyClient.getByIDs.mockResolvedValueOnce(getPackagePoliciesResponse()); + const trustedAppByPolicy = getTrustedAppByPolicy(); + await expect( + updateTrustedApp( + exceptionsListClient, + savedObjectClient, + packagePolicyClient, + trustedAppByPolicy.id, + toUpdateTrustedApp(trustedAppByPolicy as MaybeImmutable), + false + ) + ).rejects.toBeInstanceOf(EndpointLicenseError); + }); }); describe('getTrustedApp', () => { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.ts index a427f13859f03..7cbdbceaf24cc 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.ts @@ -8,7 +8,7 @@ import type { SavedObjectsClientContract } from 'kibana/server'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { ENDPOINT_TRUSTED_APPS_LIST_ID } from '@kbn/securitysolution-list-constants'; -import { isEmpty } from 'lodash/fp'; +import { isEmpty, isEqual } from 'lodash/fp'; import { ExceptionListClient } from '../../../../../lists/server'; import { @@ -22,6 +22,7 @@ import { PutTrustedAppUpdateRequest, PutTrustedAppUpdateResponse, GetTrustedAppsSummaryRequest, + TrustedApp, } from '../../../../common/endpoint/types'; import { @@ -37,6 +38,7 @@ import { } from './errors'; import { PackagePolicyServiceInterface } from '../../../../../fleet/server'; import { PackagePolicy } from '../../../../../fleet/common'; +import { EndpointLicenseError } from '../../errors'; const getNonExistingPoliciesFromTrustedApp = async ( savedObjectClient: SavedObjectsClientContract, @@ -63,6 +65,28 @@ const getNonExistingPoliciesFromTrustedApp = async ( return policies.filter((policy) => policy.version === undefined); }; +const isUserTryingToModifyEffectScopeWithoutPermissions = ( + currentTrustedApp: TrustedApp, + updatedTrustedApp: PutTrustedAppUpdateRequest, + isAtLeastPlatinum: boolean +): boolean => { + if (updatedTrustedApp.effectScope.type === 'global') { + return false; + } else if (isAtLeastPlatinum) { + return false; + } else if ( + isEqual( + currentTrustedApp.effectScope.type === 'policy' && + currentTrustedApp.effectScope.policies.sort(), + updatedTrustedApp.effectScope.policies.sort() + ) + ) { + return false; + } else { + return true; + } +}; + export const deleteTrustedApp = async ( exceptionsListClient: ExceptionListClient, { id }: DeleteTrustedAppsRequestParams @@ -126,11 +150,16 @@ export const createTrustedApp = async ( exceptionsListClient: ExceptionListClient, savedObjectClient: SavedObjectsClientContract, packagePolicyClient: PackagePolicyServiceInterface, - newTrustedApp: PostTrustedAppCreateRequest + newTrustedApp: PostTrustedAppCreateRequest, + isAtLeastPlatinum: boolean ): Promise => { // Ensure list is created if it does not exist await exceptionsListClient.createTrustedAppsList(); + if (newTrustedApp.effectScope.type === 'policy' && !isAtLeastPlatinum) { + throw new EndpointLicenseError(); + } + const unexistingPolicies = await getNonExistingPoliciesFromTrustedApp( savedObjectClient, packagePolicyClient, @@ -156,7 +185,8 @@ export const updateTrustedApp = async ( savedObjectClient: SavedObjectsClientContract, packagePolicyClient: PackagePolicyServiceInterface, id: string, - updatedTrustedApp: PutTrustedAppUpdateRequest + updatedTrustedApp: PutTrustedAppUpdateRequest, + isAtLeastPlatinum: boolean ): Promise => { const currentTrustedApp = await exceptionsListClient.getExceptionListItem({ itemId: '', @@ -168,6 +198,16 @@ export const updateTrustedApp = async ( throw new TrustedAppNotFoundError(id); } + if ( + isUserTryingToModifyEffectScopeWithoutPermissions( + exceptionListItemToTrustedApp(currentTrustedApp), + updatedTrustedApp, + isAtLeastPlatinum + ) + ) { + throw new EndpointLicenseError(); + } + const unexistingPolicies = await getNonExistingPoliciesFromTrustedApp( savedObjectClient, packagePolicyClient, From 2e87de963d1f1760de56556d9136a62556605398 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 14:03:49 -0400 Subject: [PATCH 16/51] fix missing nested property when storing in local storage (#113538) (#113551) Co-authored-by: Sandra G --- x-pack/plugins/monitoring/public/application/hooks/use_table.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_table.ts b/x-pack/plugins/monitoring/public/application/hooks/use_table.ts index 600ac20cd0199..8c27a826564ee 100644 --- a/x-pack/plugins/monitoring/public/application/hooks/use_table.ts +++ b/x-pack/plugins/monitoring/public/application/hooks/use_table.ts @@ -112,7 +112,7 @@ export function useTable(storageKey: string) { setSorting(cleanSortingData({ sort })); setLocalStorageData(storage, { page, - sort, + sort: { sort }, }); }; From 53427d92aaf58868b8b99ba5a1c046aadebe84c0 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 14:32:10 -0400 Subject: [PATCH 17/51] [Lens] refactor - move debounce one layer up for thresholds (remove updater) (#113222) (#113554) * [Lens] remove updater from vis * removing blur handle * fix * blur fix Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Marta Bondyra --- .../editor_frame/state_helpers.ts | 4 +- .../pie_visualization/render_function.tsx | 6 +- .../state_management/init_middleware/index.ts | 6 +- .../state_management/optimizing_middleware.ts | 4 +- .../lens/public/state_management/selectors.ts | 6 +- .../public/xy_visualization/expression.tsx | 10 +-- .../xy_config_panel/threshold_panel.tsx | 79 +++++++++++-------- 7 files changed, 61 insertions(+), 54 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts index 5e059a1e2e8b1..a09cf269f753c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts @@ -57,7 +57,7 @@ export async function initializeDatasources( return states; } -export const createDatasourceLayers = memoizeOne(function createDatasourceLayers( +export const getDatasourceLayers = memoizeOne(function getDatasourceLayers( datasourceStates: DatasourceStates, datasourceMap: DatasourceMap ) { @@ -111,7 +111,7 @@ export async function persistedStateToExpression( { isFullEditor: false } ); - const datasourceLayers = createDatasourceLayers(datasourceStates, datasourceMap); + const datasourceLayers = getDatasourceLayers(datasourceStates, datasourceMap); const datasourceId = getActiveDatasourceIdFromDoc(doc); if (datasourceId == null) { diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx index e79066b0145d2..8babb11c856b1 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx @@ -215,11 +215,11 @@ export function PieComponent( }, }); - const [state, setState] = useState({ isReady: false }); + const [isReady, setIsReady] = useState(false); // It takes a cycle for the chart to render. This prevents // reporting from printing a blank chart placeholder. useEffect(() => { - setState({ isReady: true }); + setIsReady(true); }, []); const hasNegative = firstTable.rows.some((row) => { @@ -273,7 +273,7 @@ export function PieComponent( reportTitle={props.args.title} reportDescription={props.args.description} className="lnsPieExpression__container" - isReady={state.isReady} + isReady={isReady} > (store: MiddlewareAP store.dispatch ); return (next: Dispatch) => (action: PayloadAction) => { - if (lensSlice.actions.loadInitial.match(action)) { + if (loadInitialAction.match(action)) { return loadInitial(store, storeDeps, action.payload); - } else if (lensSlice.actions.navigateAway.match(action)) { + } else if (navigateAway.match(action)) { return unsubscribeFromExternalContext(); } next(action); diff --git a/x-pack/plugins/lens/public/state_management/optimizing_middleware.ts b/x-pack/plugins/lens/public/state_management/optimizing_middleware.ts index f1293a84255b9..610be69166ba7 100644 --- a/x-pack/plugins/lens/public/state_management/optimizing_middleware.ts +++ b/x-pack/plugins/lens/public/state_management/optimizing_middleware.ts @@ -7,12 +7,12 @@ import { Dispatch, MiddlewareAPI, Action } from '@reduxjs/toolkit'; import { isEqual } from 'lodash'; -import { lensSlice } from './lens_slice'; +import { onActiveDataChange } from '.'; /** cancels updates to the store that don't change the state */ export const optimizingMiddleware = () => (store: MiddlewareAPI) => { return (next: Dispatch) => (action: Action) => { - if (lensSlice.actions.onActiveDataChange.match(action)) { + if (onActiveDataChange.match(action)) { if (isEqual(store.getState().lens.activeData, action.payload)) { return; } diff --git a/x-pack/plugins/lens/public/state_management/selectors.ts b/x-pack/plugins/lens/public/state_management/selectors.ts index 360ca48c2d279..c1d1700d8b3b5 100644 --- a/x-pack/plugins/lens/public/state_management/selectors.ts +++ b/x-pack/plugins/lens/public/state_management/selectors.ts @@ -10,7 +10,7 @@ import { SavedObjectReference } from 'kibana/server'; import { LensState } from './types'; import { extractFilterReferences } from '../persistence'; import { Datasource, DatasourceMap, VisualizationMap } from '../types'; -import { createDatasourceLayers } from '../editor_frame_service/editor_frame'; +import { getDatasourceLayers } from '../editor_frame_service/editor_frame'; export const selectPersistedDoc = (state: LensState) => state.lens.persistedDoc; export const selectQuery = (state: LensState) => state.lens.query; @@ -141,13 +141,13 @@ export const selectAreDatasourcesLoaded = createSelector( export const selectDatasourceLayers = createSelector( [selectDatasourceStates, selectDatasourceMap], - (datasourceStates, datasourceMap) => createDatasourceLayers(datasourceStates, datasourceMap) + (datasourceStates, datasourceMap) => getDatasourceLayers(datasourceStates, datasourceMap) ); export const selectFramePublicAPI = createSelector( [selectDatasourceStates, selectActiveData, selectDatasourceMap], (datasourceStates, activeData, datasourceMap) => ({ - datasourceLayers: createDatasourceLayers(datasourceStates, datasourceMap), + datasourceLayers: getDatasourceLayers(datasourceStates, datasourceMap), activeData, }) ); diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index 863289c31bba4..bc80b4569977c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -191,20 +191,18 @@ function getIconForSeriesType(seriesType: SeriesType): IconType { const MemoizedChart = React.memo(XYChart); export function XYChartReportable(props: XYChartRenderProps) { - const [state, setState] = useState({ - isReady: false, - }); + const [isReady, setIsReady] = useState(false); // It takes a cycle for the XY chart to render. This prevents // reporting from printing a blank chart placeholder. useEffect(() => { - setState({ isReady: true }); - }, [setState]); + setIsReady(true); + }, [setIsReady]); return ( diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/threshold_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/threshold_panel.tsx index 1e5b90e41b623..087eee9005c06 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/threshold_panel.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/threshold_panel.tsx @@ -6,12 +6,12 @@ */ import './xy_config_panel.scss'; -import React, { useCallback } from 'react'; +import React, { useCallback, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiComboBox, EuiFormRow, EuiIcon, EuiRange } from '@elastic/eui'; import type { PaletteRegistry } from 'src/plugins/charts/public'; import type { VisualizationDimensionEditorProps } from '../../types'; -import { State } from '../types'; +import { State, XYState } from '../types'; import { FormatFactory } from '../../../common'; import { YConfig } from '../../../common/expressions'; import { LineStyle, FillStyle } from '../../../common/expressions/xy_chart'; @@ -116,29 +116,35 @@ export const ThresholdPanel = ( } ) => { const { state, setState, layerId, accessor } = props; - const index = state.layers.findIndex((l) => l.layerId === layerId); - const layer = state.layers[index]; + + const { inputValue: localState, handleInputChange: setLocalState } = useDebouncedValue({ + value: state, + onChange: setState, + }); + + const index = localState.layers.findIndex((l) => l.layerId === layerId); + const layer = localState.layers[index]; const setYConfig = useCallback( (yConfig: Partial | undefined) => { if (yConfig == null) { return; } - setState((currState) => { - const currLayer = currState.layers[index]; - const newYConfigs = [...(currLayer.yConfig || [])]; - const existingIndex = newYConfigs.findIndex( - (yAxisConfig) => yAxisConfig.forAccessor === accessor - ); - if (existingIndex !== -1) { - newYConfigs[existingIndex] = { ...newYConfigs[existingIndex], ...yConfig }; - } else { - newYConfigs.push({ forAccessor: accessor, ...yConfig }); - } - return updateLayer(currState, { ...currLayer, yConfig: newYConfigs }, index); - }); + const newYConfigs = [...(layer.yConfig || [])]; + const existingIndex = newYConfigs.findIndex( + (yAxisConfig) => yAxisConfig.forAccessor === accessor + ); + if (existingIndex !== -1) { + newYConfigs[existingIndex] = { ...newYConfigs[existingIndex], ...yConfig }; + } else { + newYConfigs.push({ + forAccessor: accessor, + ...yConfig, + }); + } + setLocalState(updateLayer(localState, { ...layer, yConfig: newYConfigs }, index)); }, - [accessor, index, setState] + [accessor, index, localState, layer, setLocalState] ); const currentYConfig = layer.yConfig?.find((yConfig) => yConfig.forAccessor === accessor); @@ -291,35 +297,38 @@ const LineThicknessSlider = ({ value: number; onChange: (value: number) => void; }) => { - const onChangeWrapped = useCallback( - (newValue) => { - if (Number.isInteger(newValue)) { - onChange(getSafeValue(newValue, newValue, minRange, maxRange)); - } - }, - [onChange] - ); - const { inputValue, handleInputChange } = useDebouncedValue( - { value, onChange: onChangeWrapped }, - { allowFalsyValue: true } - ); + const [unsafeValue, setUnsafeValue] = useState(String(value)); return ( { - const newValue = e.currentTarget.value; - handleInputChange(newValue === '' ? '' : Number(newValue)); + onChange={({ currentTarget: { value: newValue } }) => { + setUnsafeValue(newValue); + const convertedValue = newValue === '' ? '' : Number(newValue); + const safeValue = getSafeValue(Number(newValue), Number(newValue), minRange, maxRange); + // only update onChange is the value is valid and in range + if (convertedValue === safeValue) { + onChange(safeValue); + } }} onBlur={() => { - handleInputChange(getSafeValue(inputValue, value, minRange, maxRange)); + if (unsafeValue !== String(value)) { + const safeValue = getSafeValue( + unsafeValue === '' ? unsafeValue : Number(unsafeValue), + value, + minRange, + maxRange + ); + onChange(safeValue); + setUnsafeValue(String(safeValue)); + } }} /> ); From f095537175840dafa0d18c7b441d10acc3606265 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 15:29:48 -0400 Subject: [PATCH 18/51] [eslint] forbid trailing slashes in package imports (#113455) (#113558) Co-authored-by: spalger Co-authored-by: Spencer Co-authored-by: spalger --- .../elastic-eslint-config-kibana/.eslintrc.js | 1 + packages/kbn-eslint-plugin-eslint/index.js | 1 + .../rules/no_trailing_import_slash.js | 37 +++++++++ .../rules/no_trailing_import_slash.test.js | 75 +++++++++++++++++++ .../components/pager/tool_bar_pagination.tsx | 2 +- .../application/mount_management_section.ts | 2 +- .../reporting/server/config/create_config.ts | 2 +- 7 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 packages/kbn-eslint-plugin-eslint/rules/no_trailing_import_slash.js create mode 100644 packages/kbn-eslint-plugin-eslint/rules/no_trailing_import_slash.test.js diff --git a/packages/elastic-eslint-config-kibana/.eslintrc.js b/packages/elastic-eslint-config-kibana/.eslintrc.js index 21e17bb920e65..651496c155a8f 100644 --- a/packages/elastic-eslint-config-kibana/.eslintrc.js +++ b/packages/elastic-eslint-config-kibana/.eslintrc.js @@ -93,5 +93,6 @@ module.exports = { '@kbn/eslint/no_async_promise_body': 'error', '@kbn/eslint/no_async_foreach': 'error', + '@kbn/eslint/no_trailing_import_slash': 'error', }, }; diff --git a/packages/kbn-eslint-plugin-eslint/index.js b/packages/kbn-eslint-plugin-eslint/index.js index a37d3c762a748..22d9c752d4745 100644 --- a/packages/kbn-eslint-plugin-eslint/index.js +++ b/packages/kbn-eslint-plugin-eslint/index.js @@ -15,5 +15,6 @@ module.exports = { no_export_all: require('./rules/no_export_all'), no_async_promise_body: require('./rules/no_async_promise_body'), no_async_foreach: require('./rules/no_async_foreach'), + no_trailing_import_slash: require('./rules/no_trailing_import_slash'), }, }; diff --git a/packages/kbn-eslint-plugin-eslint/rules/no_trailing_import_slash.js b/packages/kbn-eslint-plugin-eslint/rules/no_trailing_import_slash.js new file mode 100644 index 0000000000000..bd315bee93110 --- /dev/null +++ b/packages/kbn-eslint-plugin-eslint/rules/no_trailing_import_slash.js @@ -0,0 +1,37 @@ +/* + * 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. + */ + +/** @typedef {import("eslint").Rule.RuleModule} Rule */ +/** @typedef {import("@typescript-eslint/typescript-estree").TSESTree.ImportDeclaration} ImportDeclaration */ + +const ERROR_MSG = + 'Using a trailing slash in package import statements causes issues with webpack and is inconsistent with the rest of the respository.'; + +/** @type {Rule} */ +module.exports = { + meta: { + fixable: 'code', + schema: [], + }, + create: (context) => ({ + ImportDeclaration(_) { + const node = /** @type {ImportDeclaration} */ (_); + const req = node.source.value; + + if (!req.startsWith('.') && req.endsWith('/')) { + context.report({ + message: ERROR_MSG, + loc: node.source.loc, + fix(fixer) { + return fixer.replaceText(node.source, `'${req.slice(0, -1)}'`); + }, + }); + } + }, + }), +}; diff --git a/packages/kbn-eslint-plugin-eslint/rules/no_trailing_import_slash.test.js b/packages/kbn-eslint-plugin-eslint/rules/no_trailing_import_slash.test.js new file mode 100644 index 0000000000000..0b122dfae0cf3 --- /dev/null +++ b/packages/kbn-eslint-plugin-eslint/rules/no_trailing_import_slash.test.js @@ -0,0 +1,75 @@ +/* + * 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. + */ + +const { RuleTester } = require('eslint'); +const rule = require('./no_trailing_import_slash'); +const dedent = require('dedent'); + +const ruleTester = new RuleTester({ + parser: require.resolve('@typescript-eslint/parser'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 2018, + ecmaFeatures: { + jsx: true, + }, + }, +}); + +ruleTester.run('@kbn/eslint/no_trailing_import_slash', rule, { + valid: [ + { + code: dedent` + import foo from 'bar'; + `, + }, + { + code: dedent` + import foo from './bar'; + `, + }, + { + code: dedent` + import foo from './bar/'; + `, + }, + ], + + invalid: [ + { + code: dedent` + import foo from 'bar/'; + `, + errors: [ + { + line: 1, + message: + 'Using a trailing slash in package import statements causes issues with webpack and is inconsistent with the rest of the respository.', + }, + ], + output: dedent` + import foo from 'bar'; + `, + }, + { + code: dedent` + import foo from 'bar/box/'; + `, + errors: [ + { + line: 1, + message: + 'Using a trailing slash in package import statements causes issues with webpack and is inconsistent with the rest of the respository.', + }, + ], + output: dedent` + import foo from 'bar/box'; + `, + }, + ], +}); diff --git a/src/plugins/discover/public/application/apps/main/components/doc_table/components/pager/tool_bar_pagination.tsx b/src/plugins/discover/public/application/apps/main/components/doc_table/components/pager/tool_bar_pagination.tsx index 878a9b8162628..ccdb620e0ab85 100644 --- a/src/plugins/discover/public/application/apps/main/components/doc_table/components/pager/tool_bar_pagination.tsx +++ b/src/plugins/discover/public/application/apps/main/components/doc_table/components/pager/tool_bar_pagination.tsx @@ -17,7 +17,7 @@ import { EuiPopover, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n/'; +import { i18n } from '@kbn/i18n'; interface ToolBarPaginationProps { pageSize: number; diff --git a/x-pack/plugins/index_management/public/application/mount_management_section.ts b/x-pack/plugins/index_management/public/application/mount_management_section.ts index 17ac095128a76..71e0f80365430 100644 --- a/x-pack/plugins/index_management/public/application/mount_management_section.ts +++ b/x-pack/plugins/index_management/public/application/mount_management_section.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { SemVer } from 'semver'; import { CoreSetup } from 'src/core/public'; -import { ManagementAppMountParams } from 'src/plugins/management/public/'; +import { ManagementAppMountParams } from 'src/plugins/management/public'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; import { UIM_APP_NAME } from '../../common/constants'; diff --git a/x-pack/plugins/reporting/server/config/create_config.ts b/x-pack/plugins/reporting/server/config/create_config.ts index dc90ad580ccc5..3ca13295aff4d 100644 --- a/x-pack/plugins/reporting/server/config/create_config.ts +++ b/x-pack/plugins/reporting/server/config/create_config.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { i18n } from '@kbn/i18n/'; +import { i18n } from '@kbn/i18n'; import crypto from 'crypto'; import { upperFirst } from 'lodash'; import { Observable } from 'rxjs'; From bb01ab48d08542584cc5f8cacae9f6c52d304c67 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 15:53:14 -0400 Subject: [PATCH 19/51] React beats instance view (#113500) (#113561) * Add beat instance page * Fix beat instance tabs to match angular * Remove unused cluster prop on beats template * Fix breadcrumbs * Remove unused cluster prop * Use correct codepath * Switch beats instance back to use template Doesn't buy us a lot here, but mirrors the pattern used in logstash where it's a lot better than just calling PageTemplate directly. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Mat Schaffer --- .../monitoring/public/application/index.tsx | 8 ++ .../pages/beats/beats_template.tsx | 26 ++++-- .../application/pages/beats/instance.tsx | 90 +++++++++++++++++++ .../application/pages/beats/instances.tsx | 1 - .../application/pages/beats/overview.tsx | 1 - 5 files changed, 116 insertions(+), 10 deletions(-) create mode 100644 x-pack/plugins/monitoring/public/application/pages/beats/instance.tsx diff --git a/x-pack/plugins/monitoring/public/application/index.tsx b/x-pack/plugins/monitoring/public/application/index.tsx index 690ea26319bd3..dea8d18bb65b1 100644 --- a/x-pack/plugins/monitoring/public/application/index.tsx +++ b/x-pack/plugins/monitoring/public/application/index.tsx @@ -22,6 +22,7 @@ import { NoDataPage } from './pages/no_data'; import { ElasticsearchOverviewPage } from './pages/elasticsearch/overview'; import { BeatsOverviewPage } from './pages/beats/overview'; import { BeatsInstancesPage } from './pages/beats/instances'; +import { BeatsInstancePage } from './pages/beats/instance'; import { CODE_PATH_ELASTICSEARCH, CODE_PATH_BEATS } from '../../common/constants'; import { ElasticsearchNodesPage } from './pages/elasticsearch/nodes_page'; import { ElasticsearchIndicesPage } from './pages/elasticsearch/indices_page'; @@ -111,6 +112,13 @@ const MonitoringApp: React.FC<{ /> {/* Beats Views */} + + = ({ cluster, ...props }) => { - const tabs: TabMenuItem[] = [ - { +export const BeatsTemplate: React.FC = ({ instance, ...props }) => { + const tabs: TabMenuItem[] = []; + + if (!instance) { + tabs.push({ id: 'overview', label: i18n.translate('xpack.monitoring.beatsNavigation.overviewLinkText', { defaultMessage: 'Overview', }), route: '/beats', - }, - { + }); + tabs.push({ id: 'instances', label: i18n.translate('xpack.monitoring.beatsNavigation.instancesLinkText', { defaultMessage: 'Instances', }), route: '/beats/beats', - }, - ]; + }); + } else { + tabs.push({ + id: 'overview', + label: i18n.translate('xpack.monitoring.beatsNavigation.instance.overviewLinkText', { + defaultMessage: 'Overview', + }), + route: `/beats/beat/${instance}`, + }); + } return ; }; diff --git a/x-pack/plugins/monitoring/public/application/pages/beats/instance.tsx b/x-pack/plugins/monitoring/public/application/pages/beats/instance.tsx new file mode 100644 index 0000000000000..f7ff03898fda6 --- /dev/null +++ b/x-pack/plugins/monitoring/public/application/pages/beats/instance.tsx @@ -0,0 +1,90 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useContext, useState, useCallback, useEffect } from 'react'; +import { useParams } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; +import { find } from 'lodash'; +import { ComponentProps } from '../../route_init'; +import { GlobalStateContext } from '../../global_state_context'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { useCharts } from '../../hooks/use_charts'; +// @ts-ignore +import { Beat } from '../../../components/beats/beat'; +import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { BeatsTemplate } from './beats_template'; + +export const BeatsInstancePage: React.FC = ({ clusters }) => { + const { instance }: { instance: string } = useParams(); + + const globalState = useContext(GlobalStateContext); + const { services } = useKibana<{ data: any }>(); + const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { zoomInfo, onBrush } = useCharts(); + const clusterUuid = globalState.cluster_uuid; + const ccs = globalState.ccs; + const cluster = find(clusters, { + cluster_uuid: clusterUuid, + }) as any; + const [data, setData] = useState({} as any); + const [beatName, setBeatName] = useState(''); + + const title = i18n.translate('xpack.monitoring.beats.instance.routeTitle', { + defaultMessage: 'Beats - {instanceName} - Overview', + values: { + instanceName: beatName, + }, + }); + + const pageTitle = i18n.translate('xpack.monitoring.beats.instance.pageTitle', { + defaultMessage: 'Beat instance: {beatName}', + values: { + beatName, + }, + }); + + useEffect(() => { + if (cluster) { + generateBreadcrumbs(cluster.cluster_name, { + inBeats: true, + instance: beatName, + }); + } + }, [cluster, beatName, generateBreadcrumbs]); + + const getPageData = useCallback(async () => { + const bounds = services.data?.query.timefilter.timefilter.getBounds(); + const url = `../api/monitoring/v1/clusters/${clusterUuid}/beats/beat/${instance}`; + const response = await services.http?.fetch(url, { + method: 'POST', + body: JSON.stringify({ + ccs, + timeRange: { + min: bounds.min.toISOString(), + max: bounds.max.toISOString(), + }, + }), + }); + + setData(response); + setBeatName(response.summary.name); + }, [ccs, clusterUuid, instance, services.data?.query.timefilter.timefilter, services.http]); + + return ( + +
+ +
+
+ ); +}; diff --git a/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx b/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx index 3f32e1abf9a88..873cc7e939269 100644 --- a/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx @@ -81,7 +81,6 @@ export const BeatsInstancesPage: React.FC = ({ clusters }) => { pageTitle={pageTitle} getPageData={getPageData} data-test-subj="beatsListingPage" - cluster={cluster} >
= ({ clusters }) => { pageTitle={pageTitle} getPageData={getPageData} data-test-subj="beatsOverviewPage" - cluster={cluster} >
{renderOverview(data)}
From 93201eb31f3ed537cbe339427285d58789483e0f Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 15:54:32 -0400 Subject: [PATCH 20/51] [App Search] Add Automated Curations options to Curations Settings tab (#112766) (#113560) Co-authored-by: Byron Hulcher --- .../components/automated_icon.test.tsx | 17 ++ .../curations/components/automated_icon.tsx | 26 ++ .../views/curations_settings.test.tsx | 27 -- .../curations_settings.test.tsx | 233 ++++++++++++++++++ .../curations_settings/curations_settings.tsx | 216 ++++++++++++++++ .../curations_settings_logic.test.ts | 221 +++++++++++++++++ .../curations_settings_logic.ts | 109 ++++++++ .../index.ts} | 7 +- .../search_relevance_suggestions.test.ts | 49 ++++ .../search_relevance_suggestions.ts | 30 +++ 10 files changed, 903 insertions(+), 32 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/automated_icon.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/automated_icon.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings_logic.test.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings_logic.ts rename x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/{curations_settings.tsx => curations_settings/index.ts} (66%) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/automated_icon.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/automated_icon.test.tsx new file mode 100644 index 0000000000000..c9be6e609f37c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/automated_icon.test.tsx @@ -0,0 +1,17 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { AutomatedIcon } from './automated_icon'; + +describe('AutomatedIcon', () => { + it('renders', () => { + expect(shallow().is('svg')).toBe(true); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/automated_icon.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/automated_icon.tsx new file mode 100644 index 0000000000000..d50cf101e6059 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/automated_icon.tsx @@ -0,0 +1,26 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +export const AutomatedIcon: React.FC = ({ ...props }) => ( + +); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.test.tsx deleted file mode 100644 index 855570829cce4..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.test.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import '../../../../__mocks__/react_router'; -import '../../../__mocks__/engine_logic.mock'; - -import React from 'react'; - -import { shallow } from 'enzyme'; - -import { CurationsSettings } from './curations_settings'; - -describe('CurationsSettings', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('renders empty', () => { - const wrapper = shallow(); - - expect(wrapper.isEmptyRender()).toBe(true); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx new file mode 100644 index 0000000000000..4b4e11c31d4b8 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx @@ -0,0 +1,233 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import '../../../../../__mocks__/shallow_useeffect.mock'; +import '../../../../../__mocks__/react_router'; +import '../../../../__mocks__/engine_logic.mock'; + +import { setMockActions, setMockValues } from '../../../../../__mocks__/kea_logic'; + +import React from 'react'; + +import { shallow, ShallowWrapper } from 'enzyme'; + +import { EuiButtonEmpty, EuiCallOut, EuiSwitch } from '@elastic/eui'; + +import { mountWithIntl } from '@kbn/test/jest'; + +import { Loading } from '../../../../../shared/loading'; +import { EuiButtonTo } from '../../../../../shared/react_router_helpers'; +import { DataPanel } from '../../../data_panel'; +import { LogRetentionOptions } from '../../../log_retention'; + +import { CurationsSettings } from '.'; + +const MOCK_VALUES = { + // CurationsSettingsLogic + dataLoading: false, + curationsSettings: { + enabled: true, + mode: 'automatic', + }, + // LogRetentionLogic + isLogRetentionUpdating: false, + logRetention: { + [LogRetentionOptions.Analytics]: { + enabled: true, + }, + }, + // LicensingLogic + hasPlatinumLicense: true, +}; + +const MOCK_ACTIONS = { + // CurationsSettingsLogic + loadCurationsSettings: jest.fn(), + onSkipLoadingCurationsSettings: jest.fn(), + toggleCurationsEnabled: jest.fn(), + toggleCurationsMode: jest.fn(), + // LogRetentionLogic + fetchLogRetention: jest.fn(), +}; + +describe('CurationsSettings', () => { + beforeEach(() => { + jest.clearAllMocks(); + setMockActions(MOCK_ACTIONS); + }); + + it('loads curations and log retention settings on load', () => { + setMockValues(MOCK_VALUES); + mountWithIntl(); + + expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalled(); + expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalled(); + }); + + it('contains a switch to toggle curations settings', () => { + let wrapper: ShallowWrapper; + + setMockValues({ + ...MOCK_VALUES, + curationsSettings: { ...MOCK_VALUES.curationsSettings, enabled: true }, + }); + wrapper = shallow(); + + expect(wrapper.find(EuiSwitch).at(0).prop('checked')).toBe(true); + + setMockValues({ + ...MOCK_VALUES, + curationsSettings: { ...MOCK_VALUES.curationsSettings, enabled: false }, + }); + wrapper = shallow(); + + expect(wrapper.find(EuiSwitch).at(0).prop('checked')).toBe(false); + + wrapper.find(EuiSwitch).at(0).simulate('change'); + expect(MOCK_ACTIONS.toggleCurationsEnabled).toHaveBeenCalled(); + }); + + it('contains a switch to toggle the curations mode', () => { + let wrapper: ShallowWrapper; + + setMockValues({ + ...MOCK_VALUES, + curationsSettings: { ...MOCK_VALUES.curationsSettings, mode: 'automatic' }, + }); + wrapper = shallow(); + + expect(wrapper.find(EuiSwitch).at(1).prop('checked')).toBe(true); + + setMockValues({ + ...MOCK_VALUES, + curationsSettings: { ...MOCK_VALUES.curationsSettings, mode: 'manual' }, + }); + wrapper = shallow(); + + expect(wrapper.find(EuiSwitch).at(1).prop('checked')).toBe(false); + + wrapper.find(EuiSwitch).at(1).simulate('change'); + expect(MOCK_ACTIONS.toggleCurationsMode).toHaveBeenCalled(); + }); + + it('enables form elements and hides the callout when analytics retention is enabled', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: { + [LogRetentionOptions.Analytics]: { + enabled: true, + }, + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(EuiSwitch).at(0).prop('disabled')).toBe(false); + expect(wrapper.find(EuiSwitch).at(1).prop('disabled')).toBe(false); + expect(wrapper.find(EuiCallOut)).toHaveLength(0); + }); + + it('display a callout and disables form elements when analytics retention is disabled', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: { + [LogRetentionOptions.Analytics]: { + enabled: false, + }, + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(EuiSwitch).at(0).prop('disabled')).toBe(true); + expect(wrapper.find(EuiSwitch).at(1).prop('disabled')).toBe(true); + expect(wrapper.find(EuiCallOut).dive().find(EuiButtonTo).prop('to')).toEqual('/settings'); + }); + + it('returns a loading state when curations data is loading', () => { + setMockValues({ + ...MOCK_VALUES, + dataLoading: true, + }); + const wrapper = shallow(); + + expect(wrapper.is(Loading)).toBe(true); + }); + + it('returns a loading state when log retention data is loading', () => { + setMockValues({ + ...MOCK_VALUES, + isLogRetentionUpdating: true, + }); + const wrapper = shallow(); + + expect(wrapper.is(Loading)).toBe(true); + }); + + describe('loading curation settings based on log retention', () => { + it('loads curation settings when log retention is enabled', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: { + [LogRetentionOptions.Analytics]: { + enabled: true, + }, + }, + }); + + shallow(); + + expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(1); + }); + + it('skips loading curation settings when log retention is enabled', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: { + [LogRetentionOptions.Analytics]: { + enabled: false, + }, + }, + }); + + shallow(); + + expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(1); + }); + + it('takes no action if log retention has not yet been loaded', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: null, + }); + + shallow(); + + expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(0); + expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(0); + }); + }); + + describe('when the user has no platinum license', () => { + beforeEach(() => { + setMockValues({ + ...MOCK_VALUES, + hasPlatinumLicense: false, + }); + }); + + it('it does not fetch log retention', () => { + shallow(); + expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalledTimes(0); + }); + + it('shows a CTA to upgrade your license when the user when the user', () => { + const wrapper = shallow(); + expect(wrapper.is(DataPanel)).toBe(true); + expect(wrapper.prop('action').props.to).toEqual('/app/management/stack/license_management'); + expect(wrapper.find(EuiButtonEmpty).prop('href')).toEqual('/license-management.html'); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx new file mode 100644 index 0000000000000..de669298b11d9 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx @@ -0,0 +1,216 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect } from 'react'; + +import { useActions, useValues } from 'kea'; + +import { + EuiButtonEmpty, + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiSpacer, + EuiSwitch, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { FormattedMessage } from '@kbn/i18n/react'; + +import { docLinks } from '../../../../../shared/doc_links'; +import { LicensingLogic } from '../../../../../shared/licensing'; +import { Loading } from '../../../../../shared/loading'; +import { EuiButtonTo } from '../../../../../shared/react_router_helpers'; +import { SETTINGS_PATH } from '../../../../routes'; +import { DataPanel } from '../../../data_panel'; +import { LogRetentionLogic, LogRetentionOptions } from '../../../log_retention'; + +import { AutomatedIcon } from '../../components/automated_icon'; + +import { CurationsSettingsLogic } from './curations_settings_logic'; + +export const CurationsSettings: React.FC = () => { + const { hasPlatinumLicense } = useValues(LicensingLogic); + + const { + curationsSettings: { enabled, mode }, + dataLoading, + } = useValues(CurationsSettingsLogic); + const { + loadCurationsSettings, + onSkipLoadingCurationsSettings, + toggleCurationsEnabled, + toggleCurationsMode, + } = useActions(CurationsSettingsLogic); + + const { isLogRetentionUpdating, logRetention } = useValues(LogRetentionLogic); + const { fetchLogRetention } = useActions(LogRetentionLogic); + + const analyticsDisabled = !logRetention?.[LogRetentionOptions.Analytics].enabled; + + useEffect(() => { + if (hasPlatinumLicense) { + fetchLogRetention(); + } + }, [hasPlatinumLicense]); + + useEffect(() => { + if (logRetention) { + if (!analyticsDisabled) { + loadCurationsSettings(); + } else { + onSkipLoadingCurationsSettings(); + } + } + }, [logRetention]); + + if (!hasPlatinumLicense) + return ( + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.curations.settings.licenseUpgradeCTATitle', + { + defaultMessage: 'Introducing automated curations', + } + )} + + } + subtitle={ + + {i18n.translate('xpack.enterpriseSearch.appSearch.curations.settings.platinum', { + defaultMessage: 'Platinum', + })} + + ), + }} + /> + } + action={ + + {i18n.translate( + 'xpack.enterpriseSearch.curations.settings.start30DayTrialButtonLabel', + { + defaultMessage: 'Start a 30-day trial', + } + )} + + } + > + + {i18n.translate('xpack.enterpriseSearch.curations.settings.licenseUpgradeLink', { + defaultMessage: 'Learn more about license upgrades', + })} + + + ); + if (dataLoading || isLogRetentionUpdating) return ; + + return ( + <> + + + + + + +

+ {i18n.translate( + 'xpack.enterpriseSearch.appSearch.curations.settings.automaticCurationsTitle', + { + defaultMessage: 'Automated Curations', + } + )} +

+
+
+
+ + {analyticsDisabled && ( + <> + +

+ {i18n.translate( + 'xpack.enterpriseSearch.appSearch.curations.settings.analyticsDisabledCalloutDescription', + { + defaultMessage: + 'Automated curations require analytics to be enabled on your account.', + } + )} +

+ + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.curations.settings.manageAnalyticsButtonLabel', + { defaultMessage: 'Manage analytics' } + )} + +
+ + + )} + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.curations.settings.automaticCurationsDescription', + { + defaultMessage: + "Suggested curations will monitor your engine's analytics and make automatic suggestions to help you deliver the most relevant results. Each suggested curation can be accepted, rejected, or modified.", + } + )} + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings_logic.test.ts new file mode 100644 index 0000000000000..818fac3d0706e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings_logic.test.ts @@ -0,0 +1,221 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + LogicMounter, + mockHttpValues, + mockFlashMessageHelpers, +} from '../../../../../__mocks__/kea_logic'; +import '../../../../__mocks__/engine_logic.mock'; + +import { nextTick } from '@kbn/test/jest'; + +import { CurationsSettingsLogic } from './curations_settings_logic'; + +const DEFAULT_VALUES = { + dataLoading: true, + curationsSettings: { + enabled: false, + mode: 'manual', + }, +}; + +describe('CurationsSettingsLogic', () => { + const { mount } = new LogicMounter(CurationsSettingsLogic); + const { http } = mockHttpValues; + const { flashAPIErrors } = mockFlashMessageHelpers; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('has correct default values', () => { + mount(); + expect(CurationsSettingsLogic.values).toEqual(DEFAULT_VALUES); + }); + + describe('actions', () => { + describe('onCurationsSettingsLoad', () => { + it('saves curation settings and that data has loaded', () => { + mount(); + + CurationsSettingsLogic.actions.onCurationsSettingsLoad({ + enabled: true, + mode: 'automatic', + }); + + expect(CurationsSettingsLogic.values.dataLoading).toEqual(false); + expect(CurationsSettingsLogic.values.curationsSettings).toEqual({ + enabled: true, + mode: 'automatic', + }); + }); + }); + + describe('onSkipCurationsSettingsLoad', () => { + it('saves that data has loaded', () => { + mount(); + + CurationsSettingsLogic.actions.onSkipLoadingCurationsSettings(); + + expect(CurationsSettingsLogic.values.dataLoading).toEqual(false); + }); + }); + }); + + describe('listeners', () => { + describe('loadCurationsSettings', () => { + it('calls the curations settings API and saves the returned settings', async () => { + http.get.mockReturnValueOnce( + Promise.resolve({ + curation: { + enabled: true, + mode: 'automatic', + }, + }) + ); + mount(); + jest.spyOn(CurationsSettingsLogic.actions, 'onCurationsSettingsLoad'); + + CurationsSettingsLogic.actions.loadCurationsSettings(); + await nextTick(); + + expect(http.get).toHaveBeenCalledWith( + '/internal/app_search/engines/some-engine/search_relevance_suggestions/settings' + ); + expect(CurationsSettingsLogic.actions.onCurationsSettingsLoad).toHaveBeenCalledWith({ + enabled: true, + mode: 'automatic', + }); + }); + + it('presents any API errors to the user', async () => { + http.get.mockReturnValueOnce(Promise.reject('error')); + mount(); + + CurationsSettingsLogic.actions.loadCurationsSettings(); + await nextTick(); + + expect(flashAPIErrors).toHaveBeenCalledWith('error'); + }); + }); + + describe('toggleCurationsEnabled', () => { + it('enables curations when they are currently disabled', () => { + mount({ + curationsSettings: { + ...DEFAULT_VALUES.curationsSettings, + enabled: false, + }, + }); + jest.spyOn(CurationsSettingsLogic.actions, 'updateCurationsSetting'); + + CurationsSettingsLogic.actions.toggleCurationsEnabled(); + + expect(CurationsSettingsLogic.actions.updateCurationsSetting).toHaveBeenCalledWith({ + enabled: true, + }); + }); + + it('disables curations when they are currently enabled', () => { + mount({ + curationsSettings: { + ...DEFAULT_VALUES.curationsSettings, + enabled: true, + }, + }); + jest.spyOn(CurationsSettingsLogic.actions, 'updateCurationsSetting'); + + CurationsSettingsLogic.actions.toggleCurationsEnabled(); + + expect(CurationsSettingsLogic.actions.updateCurationsSetting).toHaveBeenCalledWith({ + enabled: false, + mode: 'manual', + }); + }); + }); + + describe('toggleCurationsMode', () => { + it('sets to manual mode when it is currently automatic', () => { + mount({ + curationsSettings: { + ...DEFAULT_VALUES.curationsSettings, + mode: 'automatic', + }, + }); + jest.spyOn(CurationsSettingsLogic.actions, 'updateCurationsSetting'); + + CurationsSettingsLogic.actions.toggleCurationsMode(); + + expect(CurationsSettingsLogic.actions.updateCurationsSetting).toHaveBeenCalledWith({ + mode: 'manual', + }); + }); + + it('sets to automatic mode when it is currently manual', () => { + mount({ + curationsSettings: { + ...DEFAULT_VALUES.curationsSettings, + mode: 'manual', + }, + }); + jest.spyOn(CurationsSettingsLogic.actions, 'updateCurationsSetting'); + + CurationsSettingsLogic.actions.toggleCurationsMode(); + + expect(CurationsSettingsLogic.actions.updateCurationsSetting).toHaveBeenCalledWith({ + mode: 'automatic', + }); + }); + }); + + describe('updateCurationsSetting', () => { + it('calls the curations settings API and saves the returned settings', async () => { + http.put.mockReturnValueOnce( + Promise.resolve({ + curation: { + enabled: true, + mode: 'automatic', + }, + }) + ); + mount(); + jest.spyOn(CurationsSettingsLogic.actions, 'onCurationsSettingsLoad'); + + CurationsSettingsLogic.actions.updateCurationsSetting({ + enabled: true, + }); + await nextTick(); + + expect(http.put).toHaveBeenCalledWith( + '/internal/app_search/engines/some-engine/search_relevance_suggestions/settings', + { + body: JSON.stringify({ + curation: { + enabled: true, + }, + }), + } + ); + expect(CurationsSettingsLogic.actions.onCurationsSettingsLoad).toHaveBeenCalledWith({ + enabled: true, + mode: 'automatic', + }); + }); + + it('presents any API errors to the user', async () => { + http.put.mockReturnValueOnce(Promise.reject('error')); + mount(); + + CurationsSettingsLogic.actions.updateCurationsSetting({}); + await nextTick(); + + expect(flashAPIErrors).toHaveBeenCalledWith('error'); + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings_logic.ts new file mode 100644 index 0000000000000..d79ad64a69788 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings_logic.ts @@ -0,0 +1,109 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { kea, MakeLogicType } from 'kea'; + +import { flashAPIErrors } from '../../../../../shared/flash_messages'; +import { HttpLogic } from '../../../../../shared/http'; +import { EngineLogic } from '../../../engine'; + +export interface CurationsSettings { + enabled: boolean; + mode: 'automatic' | 'manual'; +} + +interface CurationsSettingsValues { + dataLoading: boolean; + curationsSettings: CurationsSettings; +} + +interface CurationsSettingsActions { + loadCurationsSettings(): void; + onCurationsSettingsLoad(curationsSettings: CurationsSettings): { + curationsSettings: CurationsSettings; + }; + onSkipLoadingCurationsSettings(): void; + toggleCurationsEnabled(): void; + toggleCurationsMode(): void; + updateCurationsSetting(currationsSetting: Partial): { + currationsSetting: Partial; + }; +} + +export const CurationsSettingsLogic = kea< + MakeLogicType +>({ + path: ['enterprise_search', 'app_search', 'curations', 'curations_settings_logic'], + actions: () => ({ + loadCurationsSettings: true, + onCurationsSettingsLoad: (curationsSettings) => ({ curationsSettings }), + onSkipLoadingCurationsSettings: true, + toggleCurationsEnabled: true, + toggleCurationsMode: true, + updateCurationsSetting: (currationsSetting) => ({ currationsSetting }), + }), + reducers: () => ({ + dataLoading: [ + true, + { + onCurationsSettingsLoad: () => false, + onSkipLoadingCurationsSettings: () => false, + }, + ], + curationsSettings: [ + { + enabled: false, + mode: 'manual', + }, + { + onCurationsSettingsLoad: (_, { curationsSettings }) => curationsSettings, + }, + ], + }), + listeners: ({ actions, values }) => ({ + loadCurationsSettings: async () => { + const { http } = HttpLogic.values; + const { engineName } = EngineLogic.values; + + try { + const response = await http.get( + `/internal/app_search/engines/${engineName}/search_relevance_suggestions/settings` + ); + actions.onCurationsSettingsLoad(response.curation); + } catch (e) { + flashAPIErrors(e); + } + }, + toggleCurationsEnabled: async () => { + if (values.curationsSettings.enabled) { + actions.updateCurationsSetting({ enabled: false, mode: 'manual' }); + } else { + actions.updateCurationsSetting({ enabled: true }); + } + }, + toggleCurationsMode: async () => { + actions.updateCurationsSetting({ + mode: values.curationsSettings.mode === 'automatic' ? 'manual' : 'automatic', + }); + }, + updateCurationsSetting: async ({ currationsSetting }) => { + const { http } = HttpLogic.values; + const { engineName } = EngineLogic.values; + try { + const response = await http.put( + `/internal/app_search/engines/${engineName}/search_relevance_suggestions/settings`, + { + body: JSON.stringify({ curation: currationsSetting }), + } + ); + actions.onCurationsSettingsLoad(response.curation); + } catch (e) { + flashAPIErrors(e); + } + }, + }), +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/index.ts similarity index 66% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.tsx rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/index.ts index 4bff7f3b2ef5e..fd7d3156cc5ab 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/index.ts @@ -5,8 +5,5 @@ * 2.0. */ -import React from 'react'; - -export const CurationsSettings: React.FC = () => { - return null; -}; +export { CurationsSettings } from './curations_settings'; +export { CurationsSettingsLogic } from './curations_settings_logic'; diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/search_relevance_suggestions.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/search_relevance_suggestions.test.ts index 555a66cedc85e..d6f741526b29b 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/search_relevance_suggestions.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/search_relevance_suggestions.test.ts @@ -37,4 +37,53 @@ describe('search relevance insights routes', () => { }); }); }); + + describe('GET /internal/app_search/engines/{name}/search_relevance_suggestions/settings', () => { + const mockRouter = new MockRouter({ + method: 'get', + path: '/internal/app_search/engines/{engineName}/search_relevance_suggestions/settings', + }); + + beforeEach(() => { + registerSearchRelevanceSuggestionsRoutes({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request to enterprise search', () => { + mockRouter.callRoute({ + params: { engineName: 'some-engine' }, + }); + + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/api/as/v0/engines/:engineName/search_relevance_suggestions/settings', + }); + }); + }); + + describe('PUT /internal/app_search/engines/{name}/search_relevance_suggestions/settings', () => { + const mockRouter = new MockRouter({ + method: 'put', + path: '/internal/app_search/engines/{engineName}/search_relevance_suggestions/settings', + }); + + beforeEach(() => { + registerSearchRelevanceSuggestionsRoutes({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request to enterprise search', () => { + mockRouter.callRoute({ + params: { engineName: 'some-engine' }, + body: { curation: { enabled: true } }, + }); + + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/api/as/v0/engines/:engineName/search_relevance_suggestions/settings', + }); + }); + }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/search_relevance_suggestions.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/search_relevance_suggestions.ts index 147f68f0476ee..861d8c52b537f 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/search_relevance_suggestions.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/search_relevance_suggestions.ts @@ -7,6 +7,8 @@ import { schema } from '@kbn/config-schema'; +import { skipBodyValidation } from '../../lib/route_config_helpers'; + import { RouteDependencies } from '../../plugin'; export function registerSearchRelevanceSuggestionsRoutes({ @@ -36,4 +38,32 @@ export function registerSearchRelevanceSuggestionsRoutes({ path: '/api/as/v0/engines/:engineName/search_relevance_suggestions', }) ); + + router.get( + { + path: '/internal/app_search/engines/{engineName}/search_relevance_suggestions/settings', + validate: { + params: schema.object({ + engineName: schema.string(), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/api/as/v0/engines/:engineName/search_relevance_suggestions/settings', + }) + ); + + router.put( + skipBodyValidation({ + path: '/internal/app_search/engines/{engineName}/search_relevance_suggestions/settings', + validate: { + params: schema.object({ + engineName: schema.string(), + }), + }, + }), + enterpriseSearchRequestHandler.createRequest({ + path: '/api/as/v0/engines/:engineName/search_relevance_suggestions/settings', + }) + ); } From e63e99d9267e8fe2ad00457d6543a9738effab02 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 18:08:13 -0400 Subject: [PATCH 21/51] [Metrics UI] Add warning about redundant groupBy on metric threshold (#111891) (#113568) * [Metrics UI] Add warning about redundant groupBy on metric threshold * Fix i18n Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Zacqary Adam Xeper --- .../components/expression.tsx | 38 +++++++++++++++++++ .../metrics_explorer/components/group_by.tsx | 15 ++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx index b1a37caf149d9..6a5019d4683c8 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx @@ -260,6 +260,28 @@ export const Expressions: React.FC = (props) => { [alertParams.groupBy] ); + const groupByFilterTestPatterns = useMemo(() => { + if (!alertParams.groupBy) return null; + const groups = !Array.isArray(alertParams.groupBy) + ? [alertParams.groupBy] + : alertParams.groupBy; + return groups.map((group: string) => ({ + groupName: group, + pattern: new RegExp(`{"match(_phrase)?":{"${group}":"(.*?)"}}`), + })); + }, [alertParams.groupBy]); + + const redundantFilterGroupBy = useMemo(() => { + if (!alertParams.filterQuery || !groupByFilterTestPatterns) return []; + return groupByFilterTestPatterns + .map(({ groupName, pattern }) => { + if (pattern.test(alertParams.filterQuery!)) { + return groupName; + } + }) + .filter((g) => typeof g === 'string') as string[]; + }, [alertParams.filterQuery, groupByFilterTestPatterns]); + return ( <> @@ -425,8 +447,24 @@ export const Expressions: React.FC = (props) => { ...options, groupBy: alertParams.groupBy || undefined, }} + errorOptions={redundantFilterGroupBy} /> + {redundantFilterGroupBy.length > 0 && ( + <> + + + {redundantFilterGroupBy.join(', ')}, + groupCount: redundantFilterGroupBy.length, + }} + /> + + + )} void; fields: IFieldType[]; + errorOptions?: string[]; } -export const MetricsExplorerGroupBy = ({ options, onChange, fields }: Props) => { +export const MetricsExplorerGroupBy = ({ options, onChange, fields, errorOptions }: Props) => { const handleChange = useCallback( (selectedOptions: Array<{ label: string }>) => { const groupBy = selectedOptions.map((option) => option.label); @@ -28,9 +29,17 @@ export const MetricsExplorerGroupBy = ({ options, onChange, fields }: Props) => ); const selectedOptions = Array.isArray(options.groupBy) - ? options.groupBy.map((field) => ({ label: field })) + ? options.groupBy.map((field) => ({ + label: field, + color: errorOptions?.includes(field) ? 'danger' : undefined, + })) : options.groupBy - ? [{ label: options.groupBy }] + ? [ + { + label: options.groupBy, + color: errorOptions?.includes(options.groupBy) ? 'danger' : undefined, + }, + ] : []; return ( From 6a70f20928a6bed0bb4d9f55ad2310e2862bee58 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 18:14:39 -0400 Subject: [PATCH 22/51] trying to fix the skipped test - infra-home-page test (#113484) (#113569) * trying to fix the unskipped test * remove .only and comment Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Rashmi Kulkarni --- x-pack/test/functional/apps/infra/home_page.ts | 3 +-- x-pack/test/functional/page_objects/infra_home_page.ts | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/test/functional/apps/infra/home_page.ts b/x-pack/test/functional/apps/infra/home_page.ts index 65bfd5483d5e7..78fc57e50bf31 100644 --- a/x-pack/test/functional/apps/infra/home_page.ts +++ b/x-pack/test/functional/apps/infra/home_page.ts @@ -15,8 +15,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const pageObjects = getPageObjects(['common', 'infraHome', 'infraSavedViews']); - // FLAKY: See https://github.com/elastic/kibana/issues/106650 - describe.skip('Home page', function () { + describe('Home page', function () { this.tags('includeFirefox'); before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/empty_kibana'); diff --git a/x-pack/test/functional/page_objects/infra_home_page.ts b/x-pack/test/functional/page_objects/infra_home_page.ts index 09941c129c819..0790c694e772c 100644 --- a/x-pack/test/functional/page_objects/infra_home_page.ts +++ b/x-pack/test/functional/page_objects/infra_home_page.ts @@ -24,6 +24,7 @@ export function InfraHomePageProvider({ getService, getPageObjects }: FtrProvide ); await datePickerInput.clearValueWithKeyboard({ charByChar: true }); await datePickerInput.type([time, browser.keys.RETURN]); + await this.waitForLoading(); }, async getWaffleMap() { From d62cb560edb636423c9ca257d8a8c1c33ed9833d Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 19:52:26 -0400 Subject: [PATCH 23/51] test/functional/apps/management/_test_huge_fields.js (#112878) (#112909) Co-authored-by: Rashmi Kulkarni --- test/functional/apps/management/_test_huge_fields.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/apps/management/_test_huge_fields.js b/test/functional/apps/management/_test_huge_fields.js index c8710a79e4fc8..7b75683940928 100644 --- a/test/functional/apps/management/_test_huge_fields.js +++ b/test/functional/apps/management/_test_huge_fields.js @@ -14,7 +14,7 @@ export default function ({ getService, getPageObjects }) { const PageObjects = getPageObjects(['common', 'home', 'settings']); // FLAKY: https://github.com/elastic/kibana/issues/89031 - describe.skip('test large number of fields', function () { + describe('test large number of fields', function () { this.tags(['skipCloud']); const EXPECTED_FIELD_COUNT = '10006'; From 8d052fa86b826e0e7b7f8b0d62d2859cedc33ca4 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 20:21:07 -0400 Subject: [PATCH 24/51] [ML] Add KQL filter bar, filtering functionality, and compact design to Index data visualizer (#112870) (#113556) * [ML] Initial embed * [ML] Initial embed props * [ML] Add top nav link to data viz * Add visible fields * Add add data service to register links * Renames, refactor, use constants * Renames, refactor, use constants * Update tests and mocks * Embeddable * Update hook to update upon time udpate * Add filter support to query * Refactor filter utilities * Add filter support for embeddable * Fix saved search data undefined * Prototype aggregated view/document view switcher * Prototype flyout * Prototype save document view option in storage * Fix filter and query conflict with saved search * Minor styling edits * [ML] Update functional tests to reflect new arrow icons * [ML] Add filter buttons and KQL bars * [ML] Update filter bar onChange behavior * [ML] Update top values filter onChange behavior * [ML] Update search filters when opening saved search * [ML] Clean up * [ML] Remove fit content for height * [ML] Fix boolean legend * [ML] Fix header section when browser width is small to large and when index pattern title is too large * [ML] Hide expander icon when dimension is xs or s & css fixes * [ML] Delete embeddables because they are not use * [ML] Make doc count 0 for empty fields, update t/f test * [ML] Add unit testing for search utils * [ML] No need to - padding * [ML] Fix expand all/collapse all behavior to override individual setting * [ML] Fix functional tests should be 0/0% * [ML] Fix docs content spacing, rename classnames, add filters to Discover, lens, and maps * [ML] Fix icon styling to match Discover but have text/keyword/histogram * [ML] Fix filters not persisting after page refresh & on query change * [ML] Rename classnames to BEM style * [ML] Fix doc count for fields that exists but have no stats * [ML] Clean up unused styles * [ML] Fix eui var & icon & file geo * [ML] Fix navigating to Lens from new saved search broken * [ML] Change types back to Index pattern for 7.16 * [ML] Update not in docs content and snapshots * [ML] Fix Lens and indexRefName * [ML] Fix field icon and texts not aligned, remove span because EuiToolTip now supports EuiToken * [ML] Fix data view Co-authored-by: Quynh Nguyen <43350163+qn895@users.noreply.github.com> --- .../data_visualizer/common/constants.ts | 13 - .../data_visualizer/common/types/index.ts | 5 + .../embedded_map/_embedded_map.scss | 2 +- .../components/embedded_map/embedded_map.tsx | 6 +- .../examples_list/examples_list.tsx | 11 +- .../expanded_row/file_based_expanded_row.tsx | 5 +- .../geo_point_content/geo_point_content.tsx | 14 +- .../geo_point_content_with_map.tsx | 16 +- .../expanded_row/index_based_expanded_row.tsx | 17 +- .../field_count_panel/field_count_panel.tsx | 5 +- .../field_data_row/action_menu/actions.ts | 13 +- .../field_data_row/action_menu/lens_utils.ts | 5 +- .../field_data_row/number_content_preview.tsx | 26 +- .../field_type_icon.test.tsx.snap | 4 +- .../field_type_icon/_field_type_icon.scss | 4 + .../components/field_type_icon/_index.scss | 1 + .../field_type_icon/field_type_icon.test.tsx | 5 +- .../field_type_icon/field_type_icon.tsx | 105 +++--- .../field_types_filter/field_types_filter.tsx | 19 +- .../fields_stats_grid/fields_stats_grid.tsx | 2 +- .../multi_select_picker.tsx | 2 +- .../not_in_docs_context.tsx | 7 +- .../stats_table/_field_data_row.scss | 86 ----- .../common/components/stats_table/_index.scss | 99 ++++-- .../expanded_row_field_header.tsx | 7 +- .../components/field_count_stats/_index.scss | 11 +- .../field_count_stats/metric_fields_count.tsx | 3 +- .../field_count_stats/total_fields_count.tsx | 3 +- .../boolean_content.tsx | 21 +- .../choropleth_map.tsx | 22 +- .../field_data_expanded_row/date_content.tsx | 16 +- .../document_stats.tsx | 19 +- .../expanded_row_content.tsx | 10 +- .../expanded_row_panel.tsx | 31 ++ .../field_data_expanded_row/ip_content.tsx | 9 +- .../keyword_content.tsx | 9 +- .../number_content.tsx | 40 ++- .../field_data_expanded_row/other_content.tsx | 11 +- .../field_data_expanded_row/text_content.tsx | 14 +- .../field_data_row/column_chart.scss | 13 +- .../field_data_row/column_chart.tsx | 61 ++-- .../field_data_row/distinct_values.tsx | 21 +- .../field_data_row/document_stats.tsx | 35 +- .../field_data_row/number_content_preview.tsx | 13 +- .../field_data_row/use_column_chart.test.tsx | 4 +- .../field_data_row/use_column_chart.tsx | 20 +- .../metric_distribution_chart.tsx | 7 +- .../data_visualizer_stats_table.tsx | 224 ++++++++----- .../stats_table/types/field_data_row.ts | 2 + .../stats_table/use_table_settings.ts | 2 +- .../common/components/stats_table/utils.ts | 43 +++ .../components/top_values/_top_values.scss | 12 - .../components/top_values/top_values.tsx | 121 +++++-- .../common/util/field_types_utils.ts | 48 +++ .../actions_panel/actions_panel.tsx | 5 + .../index_data_visualizer_view/_index.scss | 1 + .../_index_data_visualizer_view.scss | 13 + .../index_data_visualizer_view.tsx | 217 +++++++----- .../components/search_panel/_index.scss | 1 + .../search_panel/field_type_filter.tsx | 16 +- .../components/search_panel/search_panel.scss | 20 ++ .../components/search_panel/search_panel.tsx | 161 +++++---- .../index_data_visualizer.tsx | 2 +- .../index_data_visualizer/locator/index.ts | 8 + .../locator/locator.test.ts | 105 ++++++ .../index_data_visualizer/locator/locator.ts | 131 ++++++++ .../types/index_data_visualizer_state.ts | 2 + .../utils/saved_search_utils.test.ts | 313 ++++++++++++++++++ .../utils/saved_search_utils.ts | 232 +++++++------ .../plugins/data_visualizer/public/plugin.ts | 5 +- .../routes/new_job/index_or_search.tsx | 1 + .../ml/public/locator/ml_locator.test.ts | 2 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../services/ml/data_visualizer_table.ts | 10 +- 75 files changed, 1748 insertions(+), 823 deletions(-) create mode 100644 x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/_field_type_icon.scss create mode 100644 x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/_index.scss delete mode 100644 x-pack/plugins/data_visualizer/public/application/common/components/stats_table/_field_data_row.scss create mode 100644 x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/expanded_row_panel.tsx create mode 100644 x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/_index.scss create mode 100644 x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/_index_data_visualizer_view.scss create mode 100644 x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/_index.scss create mode 100644 x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.scss create mode 100644 x-pack/plugins/data_visualizer/public/application/index_data_visualizer/locator/index.ts create mode 100644 x-pack/plugins/data_visualizer/public/application/index_data_visualizer/locator/locator.test.ts create mode 100644 x-pack/plugins/data_visualizer/public/application/index_data_visualizer/locator/locator.ts create mode 100644 x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.test.ts diff --git a/x-pack/plugins/data_visualizer/common/constants.ts b/x-pack/plugins/data_visualizer/common/constants.ts index 55ebdf9a196d6..f7bea807c3e61 100644 --- a/x-pack/plugins/data_visualizer/common/constants.ts +++ b/x-pack/plugins/data_visualizer/common/constants.ts @@ -33,19 +33,6 @@ export const JOB_FIELD_TYPES = { UNKNOWN: 'unknown', } as const; -export const JOB_FIELD_TYPES_OPTIONS = { - [JOB_FIELD_TYPES.BOOLEAN]: { name: 'Boolean', icon: 'tokenBoolean' }, - [JOB_FIELD_TYPES.DATE]: { name: 'Date', icon: 'tokenDate' }, - [JOB_FIELD_TYPES.GEO_POINT]: { name: 'Geo point', icon: 'tokenGeo' }, - [JOB_FIELD_TYPES.GEO_SHAPE]: { name: 'Geo shape', icon: 'tokenGeo' }, - [JOB_FIELD_TYPES.IP]: { name: 'IP address', icon: 'tokenIP' }, - [JOB_FIELD_TYPES.KEYWORD]: { name: 'Keyword', icon: 'tokenKeyword' }, - [JOB_FIELD_TYPES.NUMBER]: { name: 'Number', icon: 'tokenNumber' }, - [JOB_FIELD_TYPES.TEXT]: { name: 'Text', icon: 'tokenString' }, - [JOB_FIELD_TYPES.HISTOGRAM]: { name: 'Histogram', icon: 'tokenNumber' }, - [JOB_FIELD_TYPES.UNKNOWN]: { name: 'Unknown' }, -}; - export const OMIT_FIELDS: string[] = ['_source', '_type', '_index', '_id', '_version', '_score']; export const NON_AGGREGATABLE_FIELD_TYPES = new Set([ diff --git a/x-pack/plugins/data_visualizer/common/types/index.ts b/x-pack/plugins/data_visualizer/common/types/index.ts index 8b51142e19129..1153b45e1cce2 100644 --- a/x-pack/plugins/data_visualizer/common/types/index.ts +++ b/x-pack/plugins/data_visualizer/common/types/index.ts @@ -6,6 +6,7 @@ */ import type { SimpleSavedObject } from 'kibana/public'; +import { isPopulatedObject } from '../utils/object_utils'; export type { JobFieldType } from './job_field_type'; export type { FieldRequestConfig, @@ -27,3 +28,7 @@ export interface DataVisualizerTableState { } export type SavedSearchSavedObject = SimpleSavedObject; + +export function isSavedSearchSavedObject(arg: unknown): arg is SavedSearchSavedObject { + return isPopulatedObject(arg, ['id', 'type', 'attributes']); +} diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/embedded_map/_embedded_map.scss b/x-pack/plugins/data_visualizer/public/application/common/components/embedded_map/_embedded_map.scss index 99ee60f62bb21..a3682bfd7d96c 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/embedded_map/_embedded_map.scss +++ b/x-pack/plugins/data_visualizer/public/application/common/components/embedded_map/_embedded_map.scss @@ -1,4 +1,4 @@ -.embeddedMapContent { +.embeddedMap__content { width: 100%; height: 100%; display: flex; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/embedded_map/embedded_map.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/embedded_map/embedded_map.tsx index 13aab06640bd5..cf357a462d9b3 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/embedded_map/embedded_map.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/embedded_map/embedded_map.tsx @@ -39,7 +39,7 @@ export function EmbeddedMapComponent({ const baseLayers = useRef(); const { - services: { embeddable: embeddablePlugin, maps: mapsPlugin }, + services: { embeddable: embeddablePlugin, maps: mapsPlugin, data }, } = useDataVisualizerKibana(); const factory: @@ -73,7 +73,7 @@ export function EmbeddedMapComponent({ const input: MapEmbeddableInput = { id: htmlIdGenerator()(), attributes: { title: '' }, - filters: [], + filters: data.query.filterManager.getFilters() ?? [], hidePanelTitles: true, viewMode: ViewMode.VIEW, isLayerTOCOpen: false, @@ -143,7 +143,7 @@ export function EmbeddedMapComponent({ return (
); diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/examples_list/examples_list.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/examples_list/examples_list.tsx index 296820479437c..1aa14a88a5248 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/examples_list/examples_list.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/examples_list/examples_list.tsx @@ -11,6 +11,7 @@ import { EuiListGroup, EuiListGroupItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { ExpandedRowFieldHeader } from '../stats_table/components/expanded_row_field_header'; +import { ExpandedRowPanel } from '../stats_table/components/field_data_expanded_row/expanded_row_panel'; interface Props { examples: Array; } @@ -31,8 +32,7 @@ export const ExamplesList: FC = ({ examples }) => { examplesContent = examples.map((example, i) => { return ( @@ -41,7 +41,10 @@ export const ExamplesList: FC = ({ examples }) => { } return ( -
+ = ({ examples }) => { {examplesContent} -
+ ); }; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/file_based_expanded_row.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/file_based_expanded_row.tsx index 4517d62ec2aa1..8a9f9a25c16fa 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/file_based_expanded_row.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/file_based_expanded_row.tsx @@ -52,10 +52,7 @@ export const FileBasedDataVisualizerExpandedRow = ({ item }: { item: FileBasedFi } return ( -
+
{getCardContent()}
); diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/geo_point_content/geo_point_content.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/geo_point_content/geo_point_content.tsx index b732e542658b5..542500df71a6f 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/geo_point_content/geo_point_content.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/geo_point_content/geo_point_content.tsx @@ -6,8 +6,6 @@ */ import React, { FC, useMemo } from 'react'; - -import { EuiFlexItem } from '@elastic/eui'; import { Feature, Point } from 'geojson'; import type { FieldDataRowProps } from '../../stats_table/types/field_data_row'; import { DocumentStatsTable } from '../../stats_table/components/field_data_expanded_row/document_stats'; @@ -15,6 +13,7 @@ import { EmbeddedMapComponent } from '../../embedded_map'; import { convertWKTGeoToLonLat, getGeoPointsLayer } from './format_utils'; import { ExpandedRowContent } from '../../stats_table/components/field_data_expanded_row/expanded_row_content'; import { ExamplesList } from '../../examples_list'; +import { ExpandedRowPanel } from '../../stats_table/components/field_data_expanded_row/expanded_row_panel'; export const DEFAULT_GEO_REGEX = RegExp('(?.+) (?.+)'); @@ -63,17 +62,12 @@ export const GeoPointContent: FC = ({ config }) => { {formattedResults && Array.isArray(formattedResults.examples) && ( - - - + )} {formattedResults && Array.isArray(formattedResults.layerList) && ( - + - + )} ); diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/geo_point_content_with_map/geo_point_content_with_map.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/geo_point_content_with_map/geo_point_content_with_map.tsx index 082083fa92ff2..5da44262d29da 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/geo_point_content_with_map/geo_point_content_with_map.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/geo_point_content_with_map/geo_point_content_with_map.tsx @@ -6,7 +6,6 @@ */ import React, { FC, useEffect, useState } from 'react'; -import { EuiFlexItem } from '@elastic/eui'; import { IndexPattern } from '../../../../../../../../../src/plugins/data/common'; import { CombinedQuery } from '../../../../index_data_visualizer/types/combined_query'; import { ExpandedRowContent } from '../../stats_table/components/field_data_expanded_row/expanded_row_content'; @@ -17,6 +16,7 @@ import { useDataVisualizerKibana } from '../../../../kibana_context'; import { JOB_FIELD_TYPES } from '../../../../../../common'; import { ES_GEO_FIELD_TYPE, LayerDescriptor } from '../../../../../../../maps/common'; import { EmbeddedMapComponent } from '../../embedded_map'; +import { ExpandedRowPanel } from '../../stats_table/components/field_data_expanded_row/expanded_row_panel'; export const GeoPointContentWithMap: FC<{ config: FieldVisConfig; @@ -26,7 +26,7 @@ export const GeoPointContentWithMap: FC<{ const { stats } = config; const [layerList, setLayerList] = useState([]); const { - services: { maps: mapsPlugin }, + services: { maps: mapsPlugin, data }, } = useDataVisualizerKibana(); // Update the layer list with updated geo points upon refresh @@ -42,6 +42,7 @@ export const GeoPointContentWithMap: FC<{ indexPatternId: indexPattern.id, geoFieldName: config.fieldName, geoFieldType: config.type as ES_GEO_FIELD_TYPE, + filters: data.query.filterManager.getFilters() ?? [], query: { query: combinedQuery.searchString, language: combinedQuery.searchQueryLanguage, @@ -57,19 +58,16 @@ export const GeoPointContentWithMap: FC<{ } updateIndexPatternSearchLayer(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [indexPattern, combinedQuery, config, mapsPlugin]); + }, [indexPattern, combinedQuery, config, mapsPlugin, data.query]); if (stats?.examples === undefined) return null; return ( - - - - - + + - + ); }; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/index_based_expanded_row.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/index_based_expanded_row.tsx index ca9c8301bcfba..79af35f1c8005 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/index_based_expanded_row.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/index_based_expanded_row.tsx @@ -22,15 +22,21 @@ import { FieldVisConfig } from '../stats_table/types'; import { IndexPattern } from '../../../../../../../../src/plugins/data/common'; import { CombinedQuery } from '../../../index_data_visualizer/types/combined_query'; import { LoadingIndicator } from '../loading_indicator'; +import { IndexPatternField } from '../../../../../../../../src/plugins/data/common'; export const IndexBasedDataVisualizerExpandedRow = ({ item, indexPattern, combinedQuery, + onAddFilter, }: { item: FieldVisConfig; indexPattern: IndexPattern | undefined; combinedQuery: CombinedQuery; + /** + * Callback to add a filter to filter bar + */ + onAddFilter?: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; }) => { const config = item; const { loading, type, existsInDocs, fieldName } = config; @@ -42,7 +48,7 @@ export const IndexBasedDataVisualizerExpandedRow = ({ switch (type) { case JOB_FIELD_TYPES.NUMBER: - return ; + return ; case JOB_FIELD_TYPES.BOOLEAN: return ; @@ -61,10 +67,10 @@ export const IndexBasedDataVisualizerExpandedRow = ({ ); case JOB_FIELD_TYPES.IP: - return ; + return ; case JOB_FIELD_TYPES.KEYWORD: - return ; + return ; case JOB_FIELD_TYPES.TEXT: return ; @@ -75,10 +81,7 @@ export const IndexBasedDataVisualizerExpandedRow = ({ } return ( -
+
{loading === true ? : getCardContent()}
); diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/field_count_panel/field_count_panel.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/field_count_panel/field_count_panel.tsx index c79ed4ade7092..ce98ecd2fa5c7 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/field_count_panel/field_count_panel.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/field_count_panel/field_count_panel.tsx @@ -28,12 +28,13 @@ export const FieldCountPanel: FC = ({ - + , combinedQuery: CombinedQuery, actionFlyoutRef: MutableRefObject<(() => void | undefined) | undefined> ): Array> { - const { lens: lensPlugin, indexPatternFieldEditor } = services; + const { lens: lensPlugin, data } = services; const actions: Array> = []; + const filters = data?.query.filterManager.getFilters() ?? []; const refreshPage = () => { const refresh: Refresh = { @@ -49,7 +50,7 @@ export function getActions( available: (item: FieldVisConfig) => getCompatibleLensDataType(item.type) !== undefined && canUseLensEditor, onClick: (item: FieldVisConfig) => { - const lensAttributes = getLensAttributes(indexPattern, combinedQuery, item); + const lensAttributes = getLensAttributes(indexPattern, combinedQuery, filters, item); if (lensAttributes) { lensPlugin.navigateToPrefilledEditor({ id: `dataVisualizer-${item.fieldName}`, @@ -62,7 +63,7 @@ export function getActions( } // Allow to edit index pattern field - if (indexPatternFieldEditor?.userPermissions.editIndexPattern()) { + if (services.indexPatternFieldEditor?.userPermissions.editIndexPattern()) { actions.push({ name: i18n.translate('xpack.dataVisualizer.index.dataGrid.editIndexPatternFieldTitle', { defaultMessage: 'Edit index pattern field', @@ -76,7 +77,7 @@ export function getActions( type: 'icon', icon: 'indexEdit', onClick: (item: FieldVisConfig) => { - actionFlyoutRef.current = indexPatternFieldEditor?.openEditor({ + actionFlyoutRef.current = services.indexPatternFieldEditor?.openEditor({ ctx: { indexPattern }, fieldName: item.fieldName, onSave: refreshPage, @@ -100,7 +101,7 @@ export function getActions( return item.deletable === true; }, onClick: (item: FieldVisConfig) => { - actionFlyoutRef.current = indexPatternFieldEditor?.openDeleteModal({ + actionFlyoutRef.current = services.indexPatternFieldEditor?.openDeleteModal({ ctx: { indexPattern }, fieldName: item.fieldName!, onDelete: refreshPage, diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/field_data_row/action_menu/lens_utils.ts b/x-pack/plugins/data_visualizer/public/application/common/components/field_data_row/action_menu/lens_utils.ts index 3f80bbefcc259..615ba84afb5b7 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/field_data_row/action_menu/lens_utils.ts +++ b/x-pack/plugins/data_visualizer/public/application/common/components/field_data_row/action_menu/lens_utils.ts @@ -6,6 +6,7 @@ */ import { i18n } from '@kbn/i18n'; +import type { Filter } from '@kbn/es-query'; import type { IndexPattern } from '../../../../../../../../../src/plugins/data/common'; import type { CombinedQuery } from '../../../../index_data_visualizer/types/combined_query'; import type { @@ -15,6 +16,7 @@ import type { } from '../../../../../../../lens/public'; import { FieldVisConfig } from '../../stats_table/types'; import { JOB_FIELD_TYPES } from '../../../../../../common'; + interface ColumnsAndLayer { columns: Record; layer: XYLayerConfig; @@ -241,6 +243,7 @@ function getColumnsAndLayer( export function getLensAttributes( defaultIndexPattern: IndexPattern | undefined, combinedQuery: CombinedQuery, + filters: Filter[], item: FieldVisConfig ): TypedLensByValueInput['attributes'] | undefined { if (defaultIndexPattern === undefined || item.type === undefined || item.fieldName === undefined) @@ -279,7 +282,7 @@ export function getLensAttributes( }, }, }, - filters: [], + filters, query: { language: combinedQuery.searchQueryLanguage, query: combinedQuery.searchString }, visualization: { axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true }, diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/field_data_row/number_content_preview.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/field_data_row/number_content_preview.tsx index 08d2d42c6c027..0e38be72b20a4 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/field_data_row/number_content_preview.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/field_data_row/number_content_preview.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { FileBasedFieldVisConfig } from '../stats_table/types'; export const FileBasedNumberContentPreview = ({ config }: { config: FileBasedFieldVisConfig }) => { @@ -23,28 +23,34 @@ export const FileBasedNumberContentPreview = ({ config }: { config: FileBasedFie - + - + - + - + - + - + - {stats.min} - {stats.median} - {stats.max} + + {stats.min} + + + {stats.median} + + + {stats.max} + ); diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/__snapshots__/field_type_icon.test.tsx.snap b/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/__snapshots__/field_type_icon.test.tsx.snap index e69e2e7626718..927d8ddb7a851 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/__snapshots__/field_type_icon.test.tsx.snap +++ b/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/__snapshots__/field_type_icon.test.tsx.snap @@ -2,6 +2,7 @@ exports[`FieldTypeIcon render component when type matches a field type 1`] = ` diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/_field_type_icon.scss b/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/_field_type_icon.scss new file mode 100644 index 0000000000000..67023a4421636 --- /dev/null +++ b/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/_field_type_icon.scss @@ -0,0 +1,4 @@ +.dvFieldTypeIcon__anchor { + display: flex; + align-items: center; +} diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/_index.scss b/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/_index.scss new file mode 100644 index 0000000000000..e57d750df95ec --- /dev/null +++ b/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/_index.scss @@ -0,0 +1 @@ +@import 'field_type_icon'; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/field_type_icon.test.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/field_type_icon.test.tsx index 6b7c9eafc8c3e..b6a5ff3e5dbed 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/field_type_icon.test.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/field_type_icon.test.tsx @@ -26,11 +26,10 @@ describe('FieldTypeIcon', () => { const typeIconComponent = mount( ); - const container = typeIconComponent.find({ 'data-test-subj': 'fieldTypeIcon' }); expect(typeIconComponent.find('EuiToolTip').children()).toHaveLength(1); - container.simulate('mouseover'); + typeIconComponent.simulate('mouseover'); // Run the timers so the EuiTooltip will be visible jest.runAllTimers(); @@ -38,7 +37,7 @@ describe('FieldTypeIcon', () => { typeIconComponent.update(); expect(typeIconComponent.find('EuiToolTip').children()).toHaveLength(2); - container.simulate('mouseout'); + typeIconComponent.simulate('mouseout'); // Run the timers so the EuiTooltip will be hidden again jest.runAllTimers(); diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/field_type_icon.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/field_type_icon.tsx index ee4b4f8171d7d..3f84950c1345b 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/field_type_icon.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/field_type_icon/field_type_icon.tsx @@ -6,91 +6,62 @@ */ import React, { FC } from 'react'; - import { EuiToken, EuiToolTip } from '@elastic/eui'; - import { i18n } from '@kbn/i18n'; - import { getJobTypeAriaLabel } from '../../util/field_types_utils'; -import { JOB_FIELD_TYPES } from '../../../../../common'; import type { JobFieldType } from '../../../../../common'; +import './_index.scss'; interface FieldTypeIconProps { tooltipEnabled: boolean; type: JobFieldType; - fieldName?: string; needsAria: boolean; } interface FieldTypeIconContainerProps { ariaLabel: string | null; iconType: string; - color: string; + color?: string; needsAria: boolean; [key: string]: any; } +// defaultIcon => a unknown datatype +const defaultIcon = { iconType: 'questionInCircle', color: 'gray' }; + +// Extended & modified version of src/plugins/kibana_react/public/field_icon/field_icon.tsx +export const typeToEuiIconMap: Record = { + boolean: { iconType: 'tokenBoolean' }, + // icon for an index pattern mapping conflict in discover + conflict: { iconType: 'alert', color: 'euiColorVis9' }, + date: { iconType: 'tokenDate' }, + date_range: { iconType: 'tokenDate' }, + geo_point: { iconType: 'tokenGeo' }, + geo_shape: { iconType: 'tokenGeo' }, + ip: { iconType: 'tokenIP' }, + ip_range: { iconType: 'tokenIP' }, + // is a plugin's data type https://www.elastic.co/guide/en/elasticsearch/plugins/current/mapper-murmur3-usage.html + murmur3: { iconType: 'tokenFile' }, + number: { iconType: 'tokenNumber' }, + number_range: { iconType: 'tokenNumber' }, + histogram: { iconType: 'tokenHistogram' }, + _source: { iconType: 'editorCodeBlock', color: 'gray' }, + string: { iconType: 'tokenString' }, + text: { iconType: 'tokenString' }, + keyword: { iconType: 'tokenKeyword' }, + nested: { iconType: 'tokenNested' }, +}; + export const FieldTypeIcon: FC = ({ tooltipEnabled = false, type, - fieldName, needsAria = true, }) => { const ariaLabel = getJobTypeAriaLabel(type); - - let iconType = 'questionInCircle'; - let color = 'euiColorVis6'; - - switch (type) { - // Set icon types and colors - case JOB_FIELD_TYPES.BOOLEAN: - iconType = 'tokenBoolean'; - color = 'euiColorVis5'; - break; - case JOB_FIELD_TYPES.DATE: - iconType = 'tokenDate'; - color = 'euiColorVis7'; - break; - case JOB_FIELD_TYPES.GEO_POINT: - case JOB_FIELD_TYPES.GEO_SHAPE: - iconType = 'tokenGeo'; - color = 'euiColorVis8'; - break; - case JOB_FIELD_TYPES.TEXT: - iconType = 'document'; - color = 'euiColorVis9'; - break; - case JOB_FIELD_TYPES.IP: - iconType = 'tokenIP'; - color = 'euiColorVis3'; - break; - case JOB_FIELD_TYPES.KEYWORD: - iconType = 'tokenText'; - color = 'euiColorVis0'; - break; - case JOB_FIELD_TYPES.NUMBER: - iconType = 'tokenNumber'; - color = fieldName !== undefined ? 'euiColorVis1' : 'euiColorVis2'; - break; - case JOB_FIELD_TYPES.HISTOGRAM: - iconType = 'tokenHistogram'; - color = 'euiColorVis7'; - case JOB_FIELD_TYPES.UNKNOWN: - // Use defaults - break; - } - - const containerProps = { - ariaLabel, - iconType, - color, - needsAria, - }; + const token = typeToEuiIconMap[type] || defaultIcon; + const containerProps = { ...token, ariaLabel, needsAria }; if (tooltipEnabled === true) { - // wrap the inner component inside because EuiToolTip doesn't seem - // to support having another component directly inside the tooltip anchor - // see https://github.com/elastic/eui/issues/839 return ( = ({ defaultMessage: '{type} type', values: { type }, })} + anchorClassName="dvFieldTypeIcon__anchor" > @@ -122,12 +94,15 @@ const FieldTypeIconContainer: FC = ({ if (needsAria && ariaLabel) { wrapperProps['aria-label'] = ariaLabel; } - return ( - - - - - + ); }; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/field_types_filter/field_types_filter.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/field_types_filter/field_types_filter.tsx index 511a068f305f9..97dc2077d5931 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/field_types_filter/field_types_filter.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/field_types_filter/field_types_filter.tsx @@ -14,7 +14,7 @@ import type { FileBasedUnknownFieldVisConfig, } from '../stats_table/types/field_vis_config'; import { FieldTypeIcon } from '../field_type_icon'; -import { JOB_FIELD_TYPES_OPTIONS } from '../../../../../common'; +import { jobTypeLabels } from '../../util/field_types_utils'; interface Props { fields: Array; @@ -39,27 +39,18 @@ export const DataVisualizerFieldTypesFilter: FC = ({ const fieldTypesTracker = new Set(); const fieldTypes: Option[] = []; fields.forEach(({ type }) => { - if ( - type !== undefined && - !fieldTypesTracker.has(type) && - JOB_FIELD_TYPES_OPTIONS[type] !== undefined - ) { - const item = JOB_FIELD_TYPES_OPTIONS[type]; + if (type !== undefined && !fieldTypesTracker.has(type) && jobTypeLabels[type] !== undefined) { + const label = jobTypeLabels[type]; fieldTypesTracker.add(type); fieldTypes.push({ value: type, name: ( - {item.name} + {label} {type && ( - + )} diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/fields_stats_grid/fields_stats_grid.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/fields_stats_grid/fields_stats_grid.tsx index 8e59345c9bd63..b57072eed2944 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/fields_stats_grid/fields_stats_grid.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/fields_stats_grid/fields_stats_grid.tsx @@ -25,7 +25,7 @@ interface Props { export const getDefaultDataVisualizerListState = (): DataVisualizerTableState => ({ pageIndex: 0, - pageSize: 10, + pageSize: 25, sortField: 'fieldName', sortDirection: 'asc', visibleFieldTypes: [], diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/multi_select_picker/multi_select_picker.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/multi_select_picker/multi_select_picker.tsx index caa58009fda5d..ff4701e22953f 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/multi_select_picker/multi_select_picker.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/multi_select_picker/multi_select_picker.tsx @@ -98,7 +98,7 @@ export const MultiSelectPicker: FC<{ ); return ( - + ( - - - - + .euiTableRowCell { - border-bottom: 0; - border-top: $euiBorderThin; +@include euiBreakpoint('m', 'l', 'xl') { + .dvTable { + .columnHeader__title { + display: flex; + align-items: center; + } - } - .euiTableRow-isExpandedRow { + .columnHeader__icon { + padding-right: $euiSizeXS; + } + + .euiTableRow > .euiTableRowCell { + border-bottom: 0; + border-top: $euiBorderThin; - .euiTableRowCell { - background-color: $euiColorEmptyShade !important; - border-top: 0; - border-bottom: $euiBorderThin; - &:hover { + } + + .euiTableCellContent { + padding: $euiSizeXS; + } + + .euiTableRow-isExpandedRow { + + .euiTableRowCell { background-color: $euiColorEmptyShade !important; + border-top: 0; + border-bottom: $euiBorderThin; + &:hover { + background-color: $euiColorEmptyShade !important; + } } } - } - .dataVisualizerSummaryTable { - max-width: 350px; - min-width: 250px; - .euiTableRow > .euiTableRowCell { - border-bottom: 0; + + .dvSummaryTable { + .euiTableRow > .euiTableRowCell { + border-bottom: 0; + } + .euiTableHeaderCell { + display: none; + } + } + + .dvSummaryTable__wrapper { + min-width: $panelWidthS; + max-width: $panelWidthS; } - .euiTableHeaderCell { - display: none; + + .dvTopValues__wrapper { + min-width: fit-content; + } + + .dvPanel__wrapper { + margin: $euiSizeXS $euiSizeM $euiSizeM 0; + &.dvPanel--compressed { + width: $panelWidthS; + } + &.dvPanel--uniform { + min-width: $panelWidthS; + max-width: $panelWidthS; + } + } + + .dvMap__wrapper { + height: $euiSize * 15; //240px + } + + .dvText__wrapper { + min-width: $panelWidthS; } - } - .dataVisualizerSummaryTableWrapper { - max-width: 300px; - } - .dataVisualizerMapWrapper { - min-height: 300px; - min-width: 600px; } } diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/expanded_row_field_header/expanded_row_field_header.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/expanded_row_field_header/expanded_row_field_header.tsx index 7279bceb8be93..8fdb68c6efa4c 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/expanded_row_field_header/expanded_row_field_header.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/expanded_row_field_header/expanded_row_field_header.tsx @@ -9,7 +9,12 @@ import { EuiText } from '@elastic/eui'; import React from 'react'; export const ExpandedRowFieldHeader = ({ children }: { children: React.ReactNode }) => ( - + {children} ); diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/_index.scss b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/_index.scss index e44082c90ba32..0774cb198ea90 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/_index.scss +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/_index.scss @@ -1,3 +1,12 @@ -.dataVisualizerFieldCountContainer { +.dvFieldCount__panel { + margin-left: $euiSizeXS; + @include euiBreakpoint('xs', 's') { + flex-direction: column; + align-items: flex-start; + } +} + +.dvFieldCount__item { max-width: 300px; + min-width: 300px; } diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/metric_fields_count.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/metric_fields_count.tsx index 7996e6366c497..3b1dbf0c6376d 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/metric_fields_count.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/metric_fields_count.tsx @@ -30,8 +30,9 @@ export const MetricFieldsCount: FC = ({ metricsStats }) diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/total_fields_count.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/total_fields_count.tsx index 8e9e3e59f1281..53aa84c09d3a7 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/total_fields_count.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/total_fields_count.tsx @@ -30,8 +30,9 @@ export const TotalFieldsCount: FC = ({ fieldsCountStats } diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/boolean_content.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/boolean_content.tsx index 2869b5030f81b..754d0e470fe40 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/boolean_content.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/boolean_content.tsx @@ -6,7 +6,7 @@ */ import React, { FC, ReactNode, useMemo } from 'react'; -import { EuiBasicTable, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { EuiBasicTable, EuiSpacer, RIGHT_ALIGNMENT, HorizontalAlignment } from '@elastic/eui'; import { Axis, BarSeries, Chart, Settings } from '@elastic/charts'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -18,6 +18,7 @@ import { roundToDecimalPlace } from '../../../utils'; import { useDataVizChartTheme } from '../../hooks'; import { DocumentStatsTable } from './document_stats'; import { ExpandedRowContent } from './expanded_row_content'; +import { ExpandedRowPanel } from './expanded_row_panel'; function getPercentLabel(value: number): string { if (value === 0) { @@ -35,7 +36,7 @@ function getFormattedValue(value: number, totalCount: number): string { return `${value} (${getPercentLabel(percentage)})`; } -const BOOLEAN_DISTRIBUTION_CHART_HEIGHT = 100; +const BOOLEAN_DISTRIBUTION_CHART_HEIGHT = 70; export const BooleanContent: FC = ({ config }) => { const fieldFormat = 'fieldFormat' in config ? config.fieldFormat : undefined; @@ -68,9 +69,11 @@ export const BooleanContent: FC = ({ config }) => { ]; const summaryTableColumns = [ { + field: 'function', name: '', - render: (summaryItem: { display: ReactNode }) => summaryItem.display, - width: '75px', + render: (_: string, summaryItem: { display: ReactNode }) => summaryItem.display, + width: '25px', + align: RIGHT_ALIGNMENT as HorizontalAlignment, }, { field: 'value', @@ -90,18 +93,18 @@ export const BooleanContent: FC = ({ config }) => { - + {summaryTableTitle} - + - + = ({ config }) => { yScaleType="linear" /> - + ); }; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/choropleth_map.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/choropleth_map.tsx index 318ff655abb21..9192ea85d5868 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/choropleth_map.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/choropleth_map.tsx @@ -6,7 +6,7 @@ */ import React, { FC, useMemo } from 'react'; -import { EuiFlexItem, EuiSpacer, EuiText, htmlIdGenerator } from '@elastic/eui'; +import { EuiSpacer, EuiText, htmlIdGenerator } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { @@ -20,6 +20,7 @@ import { import { EMSTermJoinConfig } from '../../../../../../../../maps/public'; import { EmbeddedMapComponent } from '../../../embedded_map'; import { FieldVisStats } from '../../../../../../../common/types'; +import { ExpandedRowPanel } from './expanded_row_panel'; export const getChoroplethTopValuesLayer = ( fieldName: string, @@ -104,14 +105,19 @@ export const ChoroplethMap: FC = ({ stats, suggestion }) => { ); return ( - -
+ +
+ {isTopValuesSampled === true && ( - <> - - +
+ + = ({ stats, suggestion }) => { }} /> - +
)} - +
); }; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/date_content.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/date_content.tsx index 4adb76575dd48..8d5704fc16fd5 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/date_content.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/date_content.tsx @@ -6,16 +6,18 @@ */ import React, { FC, ReactNode } from 'react'; -import { EuiBasicTable, EuiFlexItem } from '@elastic/eui'; +import { EuiBasicTable, HorizontalAlignment } from '@elastic/eui'; // @ts-ignore import { formatDate } from '@elastic/eui/lib/services/format'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; +import { RIGHT_ALIGNMENT } from '@elastic/eui'; import type { FieldDataRowProps } from '../../types/field_data_row'; import { ExpandedRowFieldHeader } from '../expanded_row_field_header'; import { DocumentStatsTable } from './document_stats'; import { ExpandedRowContent } from './expanded_row_content'; +import { ExpandedRowPanel } from './expanded_row_panel'; const TIME_FORMAT = 'MMM D YYYY, HH:mm:ss.SSS'; interface SummaryTableItem { function: string; @@ -60,8 +62,10 @@ export const DateContent: FC = ({ config }) => { const summaryTableColumns = [ { name: '', - render: (summaryItem: { display: ReactNode }) => summaryItem.display, - width: '75px', + field: 'function', + render: (func: string, summaryItem: { display: ReactNode }) => summaryItem.display, + width: '70px', + align: RIGHT_ALIGNMENT as HorizontalAlignment, }, { field: 'value', @@ -73,10 +77,10 @@ export const DateContent: FC = ({ config }) => { return ( - + {summaryTableTitle} - className={'dataVisualizerSummaryTable'} + className={'dvSummaryTable'} data-test-subj={'dataVisualizerDateSummaryTable'} compressed items={summaryTableItems} @@ -84,7 +88,7 @@ export const DateContent: FC = ({ config }) => { tableCaption={summaryTableTitle} tableLayout="auto" /> - + ); }; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/document_stats.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/document_stats.tsx index f4ed74193d90a..5995b81555f9b 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/document_stats.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/document_stats.tsx @@ -8,16 +8,19 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React, { FC, ReactNode } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiBasicTable, EuiFlexItem } from '@elastic/eui'; +import { EuiBasicTable, HorizontalAlignment, RIGHT_ALIGNMENT } from '@elastic/eui'; import { ExpandedRowFieldHeader } from '../expanded_row_field_header'; import { FieldDataRowProps } from '../../types'; import { roundToDecimalPlace } from '../../../utils'; +import { ExpandedRowPanel } from './expanded_row_panel'; const metaTableColumns = [ { + field: 'function', name: '', - render: (metaItem: { display: ReactNode }) => metaItem.display, - width: '75px', + render: (_: string, metaItem: { display: ReactNode }) => metaItem.display, + width: '25px', + align: RIGHT_ALIGNMENT as HorizontalAlignment, }, { field: 'value', @@ -76,18 +79,18 @@ export const DocumentStatsTable: FC = ({ config }) => { ]; return ( - {metaTableTitle} - + ); }; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/expanded_row_content.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/expanded_row_content.tsx index a9f5dc6eaab1d..87caa0386da94 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/expanded_row_content.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/expanded_row_content.tsx @@ -6,7 +6,7 @@ */ import React, { FC, ReactNode } from 'react'; -import { EuiFlexGroup } from '@elastic/eui'; +import { EuiFlexGrid } from '@elastic/eui'; interface Props { children: ReactNode; @@ -14,12 +14,8 @@ interface Props { } export const ExpandedRowContent: FC = ({ children, dataTestSubj }) => { return ( - + {children} - + ); }; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/expanded_row_panel.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/expanded_row_panel.tsx new file mode 100644 index 0000000000000..b738dbdf67178 --- /dev/null +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/expanded_row_panel.tsx @@ -0,0 +1,31 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, ReactNode } from 'react'; +import { EuiPanel } from '@elastic/eui'; +import { EuiFlexItemProps } from '@elastic/eui/src/components/flex/flex_item'; + +interface Props { + children: ReactNode; + dataTestSubj?: string; + grow?: EuiFlexItemProps['grow']; + className?: string; +} +export const ExpandedRowPanel: FC = ({ children, dataTestSubj, grow, className }) => { + return ( + + {children} + + ); +}; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/ip_content.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/ip_content.tsx index 77cf5fad5cca8..a5db86e0c30a0 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/ip_content.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/ip_content.tsx @@ -11,7 +11,7 @@ import { TopValues } from '../../../top_values'; import { DocumentStatsTable } from './document_stats'; import { ExpandedRowContent } from './expanded_row_content'; -export const IpContent: FC = ({ config }) => { +export const IpContent: FC = ({ config, onAddFilter }) => { const { stats } = config; if (stats === undefined) return null; const { count, sampleCount, cardinality } = stats; @@ -21,7 +21,12 @@ export const IpContent: FC = ({ config }) => { return ( - + ); }; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/keyword_content.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/keyword_content.tsx index 1baea4b3f2f7c..2bae49323a6bb 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/keyword_content.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/keyword_content.tsx @@ -14,7 +14,7 @@ import { DocumentStatsTable } from './document_stats'; import { ExpandedRowContent } from './expanded_row_content'; import { ChoroplethMap } from './choropleth_map'; -export const KeywordContent: FC = ({ config }) => { +export const KeywordContent: FC = ({ config, onAddFilter }) => { const [EMSSuggestion, setEMSSuggestion] = useState(); const { stats, fieldName } = config; const fieldFormat = 'fieldFormat' in config ? config.fieldFormat : undefined; @@ -44,7 +44,12 @@ export const KeywordContent: FC = ({ config }) => { return ( - + {EMSSuggestion && stats && } ); diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/number_content.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/number_content.tsx index ef3ac5a267346..d22638af1a2eb 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/number_content.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/number_content.tsx @@ -6,7 +6,13 @@ */ import React, { FC, ReactNode, useEffect, useState } from 'react'; -import { EuiBasicTable, EuiFlexItem, EuiText } from '@elastic/eui'; +import { + EuiBasicTable, + EuiFlexItem, + EuiText, + HorizontalAlignment, + RIGHT_ALIGNMENT, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -21,8 +27,9 @@ import { TopValues } from '../../../top_values'; import { ExpandedRowFieldHeader } from '../expanded_row_field_header'; import { DocumentStatsTable } from './document_stats'; import { ExpandedRowContent } from './expanded_row_content'; +import { ExpandedRowPanel } from './expanded_row_panel'; -const METRIC_DISTRIBUTION_CHART_WIDTH = 325; +const METRIC_DISTRIBUTION_CHART_WIDTH = 260; const METRIC_DISTRIBUTION_CHART_HEIGHT = 200; interface SummaryTableItem { @@ -31,7 +38,7 @@ interface SummaryTableItem { value: number | string | undefined | null; } -export const NumberContent: FC = ({ config }) => { +export const NumberContent: FC = ({ config, onAddFilter }) => { const { stats } = config; useEffect(() => { @@ -83,7 +90,8 @@ export const NumberContent: FC = ({ config }) => { { name: '', render: (summaryItem: { display: ReactNode }) => summaryItem.display, - width: '75px', + width: '25px', + align: RIGHT_ALIGNMENT as HorizontalAlignment, }, { field: 'value', @@ -101,23 +109,33 @@ export const NumberContent: FC = ({ config }) => { return ( - + {summaryTableTitle} - className={'dataVisualizerSummaryTable'} + className={'dvSummaryTable'} compressed items={summaryTableItems} columns={summaryTableColumns} tableCaption={summaryTableTitle} data-test-subj={'dataVisualizerNumberSummaryTable'} /> - + {stats && ( - + )} {distribution && ( - + = ({ config }) => { /> - + = ({ config }) => { /> - + )} ); diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/other_content.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/other_content.tsx index 98d5cb2ec0fc9..4307da33523ed 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/other_content.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/other_content.tsx @@ -6,7 +6,6 @@ */ import React, { FC } from 'react'; -import { EuiFlexItem } from '@elastic/eui'; import type { FieldDataRowProps } from '../../types/field_data_row'; import { ExamplesList } from '../../../examples_list'; import { DocumentStatsTable } from './document_stats'; @@ -15,14 +14,12 @@ import { ExpandedRowContent } from './expanded_row_content'; export const OtherContent: FC = ({ config }) => { const { stats } = config; if (stats === undefined) return null; - return ( + return stats.count === undefined ? ( + <>{Array.isArray(stats.examples) && } + ) : ( - {Array.isArray(stats.examples) && ( - - - - )} + {Array.isArray(stats.examples) && } ); }; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/text_content.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/text_content.tsx index 700a715a33396..6f946fc1025ed 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/text_content.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/text_content.tsx @@ -6,7 +6,7 @@ */ import React, { FC, Fragment } from 'react'; -import { EuiCallOut, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { EuiCallOut, EuiSpacer, EuiFlexItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -26,7 +26,7 @@ export const TextContent: FC = ({ config }) => { return ( - + {numExamples > 0 && } {numExamples === 0 && ( @@ -44,7 +44,7 @@ export const TextContent: FC = ({ config }) => { id="xpack.dataVisualizer.dataGrid.fieldText.fieldNotPresentDescription" defaultMessage="This field was not present in the {sourceParam} field of documents queried." values={{ - sourceParam: _source, + sourceParam: _source, }} /> @@ -54,10 +54,10 @@ export const TextContent: FC = ({ config }) => { id="xpack.dataVisualizer.dataGrid.fieldText.fieldMayBePopulatedDescription" defaultMessage="It may be populated, for example, using a {copyToParam} parameter in the document mapping, or be pruned from the {sourceParam} field after indexing through the use of {includesParam} and {excludesParam} parameters." values={{ - copyToParam: copy_to, - sourceParam: _source, - includesParam: includes, - excludesParam: excludes, + copyToParam: copy_to, + sourceParam: _source, + includesParam: includes, + excludesParam: excludes, }} /> diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.scss b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.scss index 63603ee9bd2ec..8a0b9cc992c3e 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.scss +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.scss @@ -1,19 +1,22 @@ .dataGridChart__histogram { width: 100%; - height: $euiSizeXL + $euiSizeXXL; +} + +.dataGridChart__column-chart { + width: 100%; } .dataGridChart__legend { @include euiTextTruncate; - @include euiFontSizeXS; color: $euiColorMediumShade; display: block; overflow-x: hidden; - margin: $euiSizeXS 0 0 0; font-style: italic; font-weight: normal; text-align: left; + line-height: 1.1; + font-size: #{$euiFontSizeL / 2}; // 10px } .dataGridChart__legend--numeric { @@ -21,9 +24,7 @@ } .dataGridChart__legendBoolean { - width: 100%; - min-width: $euiButtonMinWidth; - td { text-align: center } + width: #{$euiSizeXS * 2.5} // 10px } /* Override to align column header to bottom of cell when no chart is available */ diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.tsx index ed4b82005db29..453754d4d6bd4 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.tsx @@ -8,7 +8,7 @@ import React, { FC } from 'react'; import classNames from 'classnames'; -import { BarSeries, Chart, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; import { EuiDataGridColumn } from '@elastic/eui'; import './column_chart.scss'; @@ -25,22 +25,9 @@ interface Props { maxChartColumns?: number; } -const columnChartTheme = { - background: { color: 'transparent' }, - chartMargins: { - left: 0, - right: 0, - top: 0, - bottom: 1, - }, - chartPaddings: { - left: 0, - right: 0, - top: 0, - bottom: 0, - }, - scales: { barsPadding: 0.1 }, -}; +const zeroSize = { bottom: 0, left: 0, right: 0, top: 0 }; +const size = { width: 100, height: 10 }; + export const ColumnChart: FC = ({ chartData, columnType, @@ -48,26 +35,34 @@ export const ColumnChart: FC = ({ hideLabel, maxChartColumns, }) => { - const { data, legendText, xScaleType } = useColumnChart(chartData, columnType, maxChartColumns); + const { data, legendText } = useColumnChart(chartData, columnType, maxChartColumns); return (
{!isUnsupportedChartData(chartData) && data.length > 0 && ( -
- - - d.datum.color} - data={data} - /> - -
+ + + { + return `${data[idx]?.key_as_string ?? ''}`; + }} + hide + /> + d.datum.color} + /> + )}
{ +interface Props { + cardinality?: number; + showIcon?: boolean; +} + +export const DistinctValues = ({ cardinality, showIcon }: Props) => { if (cardinality === undefined) return null; return ( - - - - - - {cardinality} - - + <> + {showIcon ? : null} + {cardinality} + ); }; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/document_stats.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/document_stats.tsx index 7d0bda6ac47ea..01b8f0af9538d 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/document_stats.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/document_stats.tsx @@ -5,29 +5,36 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from '@elastic/eui'; +import { EuiIcon, EuiText } from '@elastic/eui'; import React from 'react'; import type { FieldDataRowProps } from '../../types/field_data_row'; import { roundToDecimalPlace } from '../../../utils'; +import { isIndexBasedFieldVisConfig } from '../../types'; -export const DocumentStat = ({ config }: FieldDataRowProps) => { +interface Props extends FieldDataRowProps { + showIcon?: boolean; +} +export const DocumentStat = ({ config, showIcon }: Props) => { const { stats } = config; if (stats === undefined) return null; - const { count, sampleCount } = stats; - if (count === undefined || sampleCount === undefined) return null; - const docsPercent = roundToDecimalPlace((count / sampleCount) * 100); + // If field exists is docs but we don't have count stats then don't show + // Otherwise if field doesn't appear in docs at all, show 0% + const docsCount = + count ?? (isIndexBasedFieldVisConfig(config) && config.existsInDocs === true ? undefined : 0); + const docsPercent = + docsCount !== undefined && sampleCount !== undefined + ? roundToDecimalPlace((docsCount / sampleCount) * 100) + : 0; - return ( - - - - - - {count} ({docsPercent}%) + return docsCount !== undefined ? ( + <> + {showIcon ? : null} + + {docsCount} ({docsPercent}%) - - ); + + ) : null; }; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/number_content_preview.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/number_content_preview.tsx index 651e41b0cbea8..dd8685fdb9380 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/number_content_preview.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/number_content_preview.tsx @@ -6,7 +6,7 @@ */ import React, { FC, useEffect, useState } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import classNames from 'classnames'; import { MetricDistributionChart, @@ -16,8 +16,8 @@ import { import { FieldVisConfig } from '../../types'; import { kibanaFieldFormat, formatSingleValue } from '../../../utils'; -const METRIC_DISTRIBUTION_CHART_WIDTH = 150; -const METRIC_DISTRIBUTION_CHART_HEIGHT = 80; +const METRIC_DISTRIBUTION_CHART_WIDTH = 100; +const METRIC_DISTRIBUTION_CHART_HEIGHT = 10; export interface NumberContentPreviewProps { config: FieldVisConfig; @@ -59,8 +59,11 @@ export const IndexBasedNumberContentPreview: FC = ({
{legendText && ( <> - - + {kibanaFieldFormat(legendText.min, fieldFormat)} diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/use_column_chart.test.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/use_column_chart.test.tsx index aff4d6d62c6c8..228719552da9e 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/use_column_chart.test.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/use_column_chart.test.tsx @@ -122,8 +122,8 @@ describe('getLegendText()', () => { })} ); - expect(getByText('true')).toBeInTheDocument(); - expect(getByText('false')).toBeInTheDocument(); + expect(getByText('t')).toBeInTheDocument(); + expect(getByText('f')).toBeInTheDocument(); }); it('should return the chart legend text for ordinal chart data with less than max categories', () => { expect(getLegendText({ ...validOrdinalChartData, data: [{ key: 'cat', doc_count: 10 }] })).toBe( diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/use_column_chart.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/use_column_chart.tsx index 2bcf1854235d2..2c0817228655e 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/use_column_chart.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/use_column_chart.tsx @@ -94,11 +94,19 @@ export const getLegendText = ( if (chartData.type === 'boolean') { return ( - +
- {chartData.data[0] !== undefined && } - {chartData.data[1] !== undefined && } + {chartData.data[0] !== undefined && ( + + )} + {chartData.data[1] !== undefined && ( + + )}
{chartData.data[0].key_as_string}{chartData.data[1].key_as_string} + {chartData.data[0].key_as_string?.slice(0, 1) ?? ''} + + {chartData.data[1].key_as_string?.slice(0, 1) ?? ''} +
@@ -185,14 +193,16 @@ export const useColumnChart = ( // The if/else if/else is a work-around because `.map()` doesn't work with union types. // See TS Caveats for details: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-3.html#caveats if (isOrdinalChartData(chartData)) { - data = chartData.data.map((d: OrdinalDataItem) => ({ + data = chartData.data.map((d: OrdinalDataItem, idx) => ({ ...d, + x: idx, key_as_string: d.key_as_string ?? d.key, color: getColor(d), })); } else if (isNumericChartData(chartData)) { - data = chartData.data.map((d: NumericDataItem) => ({ + data = chartData.data.map((d: NumericDataItem, idx) => ({ ...d, + x: idx, key_as_string: d.key_as_string || d.key, color: getColor(d), })); diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/metric_distribution_chart/metric_distribution_chart.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/metric_distribution_chart/metric_distribution_chart.tsx index 2c4739206d47f..627c206e87fb0 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/metric_distribution_chart/metric_distribution_chart.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/metric_distribution_chart/metric_distribution_chart.tsx @@ -75,14 +75,17 @@ export const MetricDistributionChart: FC = ({ return ( ); }; return ( -
+
{ updatePageState: (update: DataVisualizerTableState) => void; getItemIdToExpandedRowMap: (itemIds: string[], items: T[]) => ItemIdToExpandedRowMap; extendedColumns?: Array>; + showPreviewByDefault?: boolean; + /** Callback to receive any updates when table or page state is changed **/ + onChange?: (update: Partial) => void; } export const DataVisualizerTable = ({ @@ -57,23 +62,52 @@ export const DataVisualizerTable = ({ updatePageState, getItemIdToExpandedRowMap, extendedColumns, + showPreviewByDefault, + onChange, }: DataVisualizerTableProps) => { const [expandedRowItemIds, setExpandedRowItemIds] = useState([]); - const [expandAll, toggleExpandAll] = useState(false); + const [expandAll, setExpandAll] = useState(false); const { onTableChange, pagination, sorting } = useTableSettings( items, pageState, updatePageState ); - const showDistributions: boolean = - ('showDistributions' in pageState && pageState.showDistributions) ?? true; - const toggleShowDistribution = () => { - updatePageState({ - ...pageState, - showDistributions: !showDistributions, - }); - }; + const [showDistributions, setShowDistributions] = useState(showPreviewByDefault ?? true); + const [dimensions, setDimensions] = useState(calculateTableColumnsDimensions()); + const [tableWidth, setTableWidth] = useState(1400); + + const toggleExpandAll = useCallback( + (shouldExpandAll: boolean) => { + setExpandedRowItemIds( + shouldExpandAll + ? // Update list of ids in expandedRowIds to include all + (items.map((item) => item.fieldName).filter((id) => id !== undefined) as string[]) + : // Otherwise, reset list of ids in expandedRowIds + [] + ); + setExpandAll(shouldExpandAll); + }, + [items] + ); + + // eslint-disable-next-line react-hooks/exhaustive-deps + const resizeHandler = useCallback( + throttle((e: { width: number; height: number }) => { + // When window or table is resized, + // update the column widths and other settings accordingly + setTableWidth(e.width); + setDimensions(calculateTableColumnsDimensions(e.width)); + }, 500), + [tableWidth] + ); + + const toggleShowDistribution = useCallback(() => { + setShowDistributions(!showDistributions); + if (onChange) { + onChange({ showDistributions: !showDistributions }); + } + }, [onChange, showDistributions]); function toggleDetails(item: DataVisualizerTableItem) { if (item.fieldName === undefined) return; @@ -90,31 +124,32 @@ export const DataVisualizerTable = ({ const columns = useMemo(() => { const expanderColumn: EuiTableComputedColumnType = { - name: ( - toggleExpandAll(!expandAll)} - aria-label={ - !expandAll - ? i18n.translate('xpack.dataVisualizer.dataGrid.expandDetailsForAllAriaLabel', { - defaultMessage: 'Expand details for all fields', - }) - : i18n.translate('xpack.dataVisualizer.dataGrid.collapseDetailsForAllAriaLabel', { - defaultMessage: 'Collapse details for all fields', - }) - } - iconType={expandAll ? 'arrowUp' : 'arrowDown'} - /> - ), + name: + dimensions.breakPoint !== 'xs' && dimensions.breakPoint !== 's' ? ( + toggleExpandAll(!expandAll)} + aria-label={ + !expandAll + ? i18n.translate('xpack.dataVisualizer.dataGrid.expandDetailsForAllAriaLabel', { + defaultMessage: 'Expand details for all fields', + }) + : i18n.translate('xpack.dataVisualizer.dataGrid.collapseDetailsForAllAriaLabel', { + defaultMessage: 'Collapse details for all fields', + }) + } + iconType={expandAll ? 'arrowDown' : 'arrowRight'} + /> + ) : null, align: RIGHT_ALIGNMENT, - width: '40px', + width: dimensions.expander, isExpander: true, render: (item: DataVisualizerTableItem) => { const displayName = item.displayName ?? item.fieldName; if (item.fieldName === undefined) return null; - const direction = expandedRowItemIds.includes(item.fieldName) ? 'arrowUp' : 'arrowDown'; + const direction = expandedRowItemIds.includes(item.fieldName) ? 'arrowDown' : 'arrowRight'; return ( ({ render: (fieldType: JobFieldType) => { return ; }, - width: '75px', + width: dimensions.type, sortable: true, align: CENTER_ALIGNMENT as HorizontalAlignment, 'data-test-subj': 'dataVisualizerTableColumnType', @@ -163,8 +198,8 @@ export const DataVisualizerTable = ({ const displayName = item.displayName ?? item.fieldName; return ( - - {displayName} + + {displayName} ); }, @@ -177,56 +212,65 @@ export const DataVisualizerTable = ({ defaultMessage: 'Documents (%)', }), render: (value: number | undefined, item: DataVisualizerTableItem) => ( - + ), sortable: (item: DataVisualizerTableItem) => item?.stats?.count, align: LEFT_ALIGNMENT as HorizontalAlignment, 'data-test-subj': 'dataVisualizerTableColumnDocumentsCount', + width: dimensions.docCount, }, { field: 'stats.cardinality', name: i18n.translate('xpack.dataVisualizer.dataGrid.distinctValuesColumnName', { defaultMessage: 'Distinct values', }), - render: (cardinality?: number) => , + render: (cardinality: number | undefined) => ( + + ), + sortable: true, align: LEFT_ALIGNMENT as HorizontalAlignment, 'data-test-subj': 'dataVisualizerTableColumnDistinctValues', + width: dimensions.distinctValues, }, { name: ( -
- +
+ {dimensions.showIcon ? ( + + ) : null} {i18n.translate('xpack.dataVisualizer.dataGrid.distributionsColumnName', { defaultMessage: 'Distributions', })} - - toggleShowDistribution()} - aria-label={ + { + - + > + toggleShowDistribution()} + aria-label={ + showDistributions + ? i18n.translate('xpack.dataVisualizer.dataGrid.showDistributionsAriaLabel', { + defaultMessage: 'Show distributions', + }) + : i18n.translate('xpack.dataVisualizer.dataGrid.hideDistributionsAriaLabel', { + defaultMessage: 'Hide distributions', + }) + } + /> + + }
), render: (item: DataVisualizerTableItem) => { @@ -252,41 +296,49 @@ export const DataVisualizerTable = ({ return null; }, + width: dimensions.distributions, align: LEFT_ALIGNMENT as HorizontalAlignment, 'data-test-subj': 'dataVisualizerTableColumnDistribution', }, ]; return extendedColumns ? [...baseColumns, ...extendedColumns] : baseColumns; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [expandAll, showDistributions, updatePageState, extendedColumns]); + }, [ + expandAll, + showDistributions, + updatePageState, + extendedColumns, + dimensions.breakPoint, + toggleExpandAll, + ]); const itemIdToExpandedRowMap = useMemo(() => { - let itemIds = expandedRowItemIds; - if (expandAll) { - itemIds = items.map((i) => i[FIELD_NAME]).filter((f) => f !== undefined) as string[]; - } + const itemIds = expandedRowItemIds; return getItemIdToExpandedRowMap(itemIds, items); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [expandAll, items, expandedRowItemIds]); + }, [items, expandedRowItemIds, getItemIdToExpandedRowMap]); return ( - - - className={'dataVisualizer'} - items={items} - itemId={FIELD_NAME} - columns={columns} - pagination={pagination} - sorting={sorting} - isExpandable={true} - itemIdToExpandedRowMap={itemIdToExpandedRowMap} - isSelectable={false} - onTableChange={onTableChange} - data-test-subj={'dataVisualizerTable'} - rowProps={(item) => ({ - 'data-test-subj': `dataVisualizerRow row-${item.fieldName}`, - })} - /> - + + {(resizeRef) => ( +
+ + className={'dvTable'} + items={items} + itemId={FIELD_NAME} + columns={columns} + pagination={pagination} + sorting={sorting} + isExpandable={true} + itemIdToExpandedRowMap={itemIdToExpandedRowMap} + isSelectable={false} + onTableChange={onTableChange} + data-test-subj={'dataVisualizerTable'} + rowProps={(item) => ({ + 'data-test-subj': `dataVisualizerRow row-${item.fieldName}`, + })} + /> +
+ )} +
); }; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/types/field_data_row.ts b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/types/field_data_row.ts index 24209af23ceb4..94b704764c93b 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/types/field_data_row.ts +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/types/field_data_row.ts @@ -6,7 +6,9 @@ */ import type { FieldVisConfig, FileBasedFieldVisConfig } from './field_vis_config'; +import { IndexPatternField } from '../../../../../../../../../src/plugins/data/common'; export interface FieldDataRowProps { config: FieldVisConfig | FileBasedFieldVisConfig; + onAddFilter?: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; } diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/use_table_settings.ts b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/use_table_settings.ts index 3fbf333bdc876..87d936edc2957 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/use_table_settings.ts +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/use_table_settings.ts @@ -10,7 +10,7 @@ import { useCallback, useMemo } from 'react'; import { DataVisualizerTableState } from '../../../../../common'; -const PAGE_SIZE_OPTIONS = [10, 25, 50]; +const PAGE_SIZE_OPTIONS = [10, 25, 50, 100]; interface UseTableSettingsReturnValue { onTableChange: EuiBasicTableProps['onChange']; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/utils.ts b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/utils.ts index 27da91153b3ba..d30a33a96c590 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/utils.ts +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/utils.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { getBreakpoint } from '@elastic/eui'; import { FileBasedFieldVisConfig } from './types'; export const getTFPercentage = (config: FileBasedFieldVisConfig) => { @@ -36,3 +37,45 @@ export const getTFPercentage = (config: FileBasedFieldVisConfig) => { falseCount, }; }; + +export const calculateTableColumnsDimensions = (width?: number) => { + const defaultSettings = { + expander: '40px', + type: '75px', + docCount: '225px', + distinctValues: '225px', + distributions: '225px', + showIcon: true, + breakPoint: 'xl', + }; + if (width === undefined) return defaultSettings; + const breakPoint = getBreakpoint(width); + switch (breakPoint) { + case 'xs': + case 's': + return { + expander: '25px', + type: '40px', + docCount: 'auto', + distinctValues: 'auto', + distributions: 'auto', + showIcon: false, + breakPoint, + }; + + case 'm': + case 'l': + return { + expander: '25px', + type: '40px', + docCount: 'auto', + distinctValues: 'auto', + distributions: 'auto', + showIcon: false, + breakPoint, + }; + + default: + return defaultSettings; + } +}; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/top_values/_top_values.scss b/x-pack/plugins/data_visualizer/public/application/common/components/top_values/_top_values.scss index 05fa1bfa94b2d..bb227dd24d48a 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/top_values/_top_values.scss +++ b/x-pack/plugins/data_visualizer/public/application/common/components/top_values/_top_values.scss @@ -4,16 +4,4 @@ .topValuesValueLabelContainer { margin-right: $euiSizeM; - &.topValuesValueLabelContainer--small { - width:70px; - } - - &.topValuesValueLabelContainer--large { - width: 200px; - } -} - -.topValuesPercentLabelContainer { - margin-left: $euiSizeM; - width:70px; } diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/top_values/top_values.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/top_values/top_values.tsx index a019f7fb0976c..45e8944c7c667 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/top_values/top_values.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/top_values/top_values.tsx @@ -12,21 +12,25 @@ import { EuiProgress, EuiSpacer, EuiText, - EuiToolTip, + EuiButtonIcon, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import classNames from 'classnames'; +import { i18n } from '@kbn/i18n'; import { roundToDecimalPlace, kibanaFieldFormat } from '../utils'; import { ExpandedRowFieldHeader } from '../stats_table/components/expanded_row_field_header'; import { FieldVisStats } from '../../../../../common/types'; +import { ExpandedRowPanel } from '../stats_table/components/field_data_expanded_row/expanded_row_panel'; +import { IndexPatternField } from '../../../../../../../../src/plugins/data/common/data_views/fields'; interface Props { stats: FieldVisStats | undefined; fieldFormat?: any; barColor?: 'primary' | 'secondary' | 'danger' | 'subdued' | 'accent'; compressed?: boolean; + onAddFilter?: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; } function getPercentLabel(docCount: number, topValuesSampleSize: number): string { @@ -38,13 +42,23 @@ function getPercentLabel(docCount: number, topValuesSampleSize: number): string } } -export const TopValues: FC = ({ stats, fieldFormat, barColor, compressed }) => { +export const TopValues: FC = ({ stats, fieldFormat, barColor, compressed, onAddFilter }) => { if (stats === undefined) return null; - const { topValues, topValuesSampleSize, topValuesSamplerShardSize, count, isTopValuesSampled } = - stats; + const { + topValues, + topValuesSampleSize, + topValuesSamplerShardSize, + count, + isTopValuesSampled, + fieldName, + } = stats; + const progressBarMax = isTopValuesSampled === true ? topValuesSampleSize : count; return ( - + = ({ stats, fieldFormat, barColor, compressed
{Array.isArray(topValues) && topValues.map((value) => ( - - - - {kibanaFieldFormat(value.key, fieldFormat)} - - - - {progressBarMax !== undefined && ( - - - {getPercentLabel(value.doc_count, progressBarMax)} - - - )} + {fieldName !== undefined && value.key !== undefined && onAddFilter !== undefined ? ( + <> + + onAddFilter( + fieldName, + typeof value.key === 'number' ? value.key.toString() : value.key, + '+' + ) + } + aria-label={i18n.translate( + 'xpack.dataVisualizer.dataGrid.field.addFilterAriaLabel', + { + defaultMessage: 'Filter for {fieldName}: "{value}"', + values: { fieldName, value: value.key }, + } + )} + data-test-subj={`dvFieldDataTopValuesAddFilterButton-${value.key}-${value.key}`} + style={{ + minHeight: 'auto', + minWidth: 'auto', + paddingRight: 2, + paddingLeft: 2, + paddingTop: 0, + paddingBottom: 0, + }} + /> + + onAddFilter( + fieldName, + typeof value.key === 'number' ? value.key.toString() : value.key, + '-' + ) + } + aria-label={i18n.translate( + 'xpack.dataVisualizer.dataGrid.field.removeFilterAriaLabel', + { + defaultMessage: 'Filter out {fieldName}: "{value}"', + values: { fieldName, value: value.key }, + } + )} + data-test-subj={`dvFieldDataTopValuesExcludeFilterButton-${value.key}-${value.key}`} + style={{ + minHeight: 'auto', + minWidth: 'auto', + paddingTop: 0, + paddingBottom: 0, + paddingRight: 2, + paddingLeft: 2, + }} + /> + + ) : null} ))} {isTopValuesSampled === true && ( - + = ({ stats, fieldFormat, barColor, compressed )}
-
+ ); }; diff --git a/x-pack/plugins/data_visualizer/public/application/common/util/field_types_utils.ts b/x-pack/plugins/data_visualizer/public/application/common/util/field_types_utils.ts index 54e0b2d5310f3..3e459cd2b079b 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/util/field_types_utils.ts +++ b/x-pack/plugins/data_visualizer/public/application/common/util/field_types_utils.ts @@ -23,6 +23,9 @@ export const jobTypeAriaLabels = { geoPointParam: 'geo point', }, }), + GEO_SHAPE: i18n.translate('xpack.dataVisualizer.fieldTypeIcon.geoShapeTypeAriaLabel', { + defaultMessage: 'geo shape type', + }), IP: i18n.translate('xpack.dataVisualizer.fieldTypeIcon.ipTypeAriaLabel', { defaultMessage: 'ip type', }), @@ -32,6 +35,9 @@ export const jobTypeAriaLabels = { NUMBER: i18n.translate('xpack.dataVisualizer.fieldTypeIcon.numberTypeAriaLabel', { defaultMessage: 'number type', }), + HISTOGRAM: i18n.translate('xpack.dataVisualizer.fieldTypeIcon.histogramTypeAriaLabel', { + defaultMessage: 'histogram type', + }), TEXT: i18n.translate('xpack.dataVisualizer.fieldTypeIcon.textTypeAriaLabel', { defaultMessage: 'text type', }), @@ -40,6 +46,48 @@ export const jobTypeAriaLabels = { }), }; +export const jobTypeLabels = { + [JOB_FIELD_TYPES.BOOLEAN]: i18n.translate('xpack.dataVisualizer.fieldTypeIcon.booleanTypeLabel', { + defaultMessage: 'Boolean', + }), + [JOB_FIELD_TYPES.DATE]: i18n.translate('xpack.dataVisualizer.fieldTypeIcon.dateTypeLabel', { + defaultMessage: 'Date', + }), + [JOB_FIELD_TYPES.GEO_POINT]: i18n.translate( + 'xpack.dataVisualizer.fieldTypeIcon.geoPointTypeLabel', + { + defaultMessage: 'Geo point', + } + ), + [JOB_FIELD_TYPES.GEO_SHAPE]: i18n.translate( + 'xpack.dataVisualizer.fieldTypeIcon.geoShapeTypeLabel', + { + defaultMessage: 'Geo shape', + } + ), + [JOB_FIELD_TYPES.IP]: i18n.translate('xpack.dataVisualizer.fieldTypeIcon.ipTypeLabel', { + defaultMessage: 'IP', + }), + [JOB_FIELD_TYPES.KEYWORD]: i18n.translate('xpack.dataVisualizer.fieldTypeIcon.keywordTypeLabel', { + defaultMessage: 'Keyword', + }), + [JOB_FIELD_TYPES.NUMBER]: i18n.translate('xpack.dataVisualizer.fieldTypeIcon.numberTypeLabel', { + defaultMessage: 'Number', + }), + [JOB_FIELD_TYPES.HISTOGRAM]: i18n.translate( + 'xpack.dataVisualizer.fieldTypeIcon.histogramTypeLabel', + { + defaultMessage: 'Histogram', + } + ), + [JOB_FIELD_TYPES.TEXT]: i18n.translate('xpack.dataVisualizer.fieldTypeIcon.textTypeLabel', { + defaultMessage: 'Text', + }), + [JOB_FIELD_TYPES.UNKNOWN]: i18n.translate('xpack.dataVisualizer.fieldTypeIcon.unknownTypeLabel', { + defaultMessage: 'Unknown', + }), +}; + export const getJobTypeAriaLabel = (type: string) => { const requestedFieldType = Object.keys(JOB_FIELD_TYPES).find( (k) => JOB_FIELD_TYPES[k as keyof typeof JOB_FIELD_TYPES] === type diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/actions_panel/actions_panel.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/actions_panel/actions_panel.tsx index bc68bdf4b6ce0..186d3ef840c21 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/actions_panel/actions_panel.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/actions_panel/actions_panel.tsx @@ -40,6 +40,7 @@ export const ActionsPanel: FC = ({ const { services: { + data, application: { capabilities }, share: { urlGenerators: { getUrlGenerator }, @@ -60,6 +61,9 @@ export const ActionsPanel: FC = ({ const state: DiscoverUrlGeneratorState = { indexPatternId, }; + + state.filters = data.query.filterManager.getFilters() ?? []; + if (searchString && searchQueryLanguage !== undefined) { state.query = { query: searchString, language: searchQueryLanguage }; } @@ -113,6 +117,7 @@ export const ActionsPanel: FC = ({ capabilities, getUrlGenerator, additionalLinks, + data.query, ]); // Note we use display:none for the DataRecognizer section as it needs to be diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/_index.scss b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/_index.scss new file mode 100644 index 0000000000000..c9b1d78320aee --- /dev/null +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/_index.scss @@ -0,0 +1 @@ +@import 'index_data_visualizer_view'; diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/_index_data_visualizer_view.scss b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/_index_data_visualizer_view.scss new file mode 100644 index 0000000000000..f49cb73454178 --- /dev/null +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/_index_data_visualizer_view.scss @@ -0,0 +1,13 @@ +.dataViewTitleHeader { + min-width: 300px; + display: flex; + flex-direction: row; + align-items: center; +} + +@include euiBreakpoint('xs', 's', 'm', 'l') { + .dataVisualizerPageHeader { + flex-direction: column; + align-items: flex-start; + } +} diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx index 0eb8e6363d607..fdd723dea3487 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx @@ -23,12 +23,12 @@ import { EuiTableActionsColumnType } from '@elastic/eui/src/components/basic_tab import { FormattedMessage } from '@kbn/i18n/react'; import { Required } from 'utility-types'; import { i18n } from '@kbn/i18n'; +import { Filter } from '@kbn/es-query'; import { - IndexPatternField, KBN_FIELD_TYPES, UI_SETTINGS, Query, - IndexPattern, + generateFilters, } from '../../../../../../../../src/plugins/data/public'; import { FullTimeRangeSelector } from '../full_time_range_selector'; import { usePageUrlState, useUrlState } from '../../../common/util/url_state'; @@ -65,10 +65,12 @@ import { DatePickerWrapper } from '../../../common/components/date_picker_wrappe import { dataVisualizerRefresh$ } from '../../services/timefilter_refresh_service'; import { HelpMenu } from '../../../common/components/help_menu'; import { TimeBuckets } from '../../services/time_buckets'; -import { extractSearchData } from '../../utils/saved_search_utils'; +import { createMergedEsQuery, getEsQueryFromSavedSearch } from '../../utils/saved_search_utils'; import { DataVisualizerIndexPatternManagement } from '../index_pattern_management'; import { ResultLink } from '../../../common/components/results_links'; import { extractErrorProperties } from '../../utils/error_utils'; +import { IndexPatternField, IndexPattern } from '../../../../../../../../src/plugins/data/common'; +import './_index.scss'; interface DataVisualizerPageState { overallStats: OverallStats; @@ -85,7 +87,7 @@ const defaultSearchQuery = { match_all: {}, }; -function getDefaultPageState(): DataVisualizerPageState { +export function getDefaultPageState(): DataVisualizerPageState { return { overallStats: { totalCount: 0, @@ -103,22 +105,25 @@ function getDefaultPageState(): DataVisualizerPageState { documentCountStats: undefined, }; } -export const getDefaultDataVisualizerListState = - (): Required => ({ - pageIndex: 0, - pageSize: 10, - sortField: 'fieldName', - sortDirection: 'asc', - visibleFieldTypes: [], - visibleFieldNames: [], - samplerShardSize: 5000, - searchString: '', - searchQuery: defaultSearchQuery, - searchQueryLanguage: SEARCH_QUERY_LANGUAGE.KUERY, - showDistributions: true, - showAllFields: false, - showEmptyFields: false, - }); +export const getDefaultDataVisualizerListState = ( + overrides?: Partial +): Required => ({ + pageIndex: 0, + pageSize: 25, + sortField: 'fieldName', + sortDirection: 'asc', + visibleFieldTypes: [], + visibleFieldNames: [], + samplerShardSize: 5000, + searchString: '', + searchQuery: defaultSearchQuery, + searchQueryLanguage: SEARCH_QUERY_LANGUAGE.KUERY, + filters: [], + showDistributions: true, + showAllFields: false, + showEmptyFields: false, + ...overrides, +}); export interface IndexDataVisualizerViewProps { currentIndexPattern: IndexPattern; @@ -129,7 +134,7 @@ const restorableDefaults = getDefaultDataVisualizerListState(); export const IndexDataVisualizerView: FC = (dataVisualizerProps) => { const { services } = useDataVisualizerKibana(); - const { docLinks, notifications, uiSettings } = services; + const { docLinks, notifications, uiSettings, data } = services; const { toasts } = notifications; const [dataVisualizerListState, setDataVisualizerListState] = usePageUrlState( @@ -150,6 +155,15 @@ export const IndexDataVisualizerView: FC = (dataVi } }, [dataVisualizerProps?.currentSavedSearch]); + useEffect(() => { + return () => { + // When navigating away from the index pattern + // Reset all previously set filters + // to make sure new page doesn't have unrelated filters + data.query.filterManager.removeAll(); + }; + }, [currentIndexPattern.id, data.query.filterManager]); + const getTimeBuckets = useCallback(() => { return new TimeBuckets({ [UI_SETTINGS.HISTOGRAM_MAX_BARS]: uiSettings.get(UI_SETTINGS.HISTOGRAM_MAX_BARS), @@ -227,13 +241,17 @@ export const IndexDataVisualizerView: FC = (dataVi const defaults = getDefaultPageState(); const { searchQueryLanguage, searchString, searchQuery } = useMemo(() => { - const searchData = extractSearchData( - currentSavedSearch, - currentIndexPattern, - uiSettings.get(UI_SETTINGS.QUERY_STRING_OPTIONS) - ); + const searchData = getEsQueryFromSavedSearch({ + indexPattern: currentIndexPattern, + uiSettings, + savedSearch: currentSavedSearch, + filterManager: data.query.filterManager, + }); if (searchData === undefined || dataVisualizerListState.searchString !== '') { + if (dataVisualizerListState.filters) { + data.query.filterManager.setFilters(dataVisualizerListState.filters); + } return { searchQuery: dataVisualizerListState.searchQuery, searchString: dataVisualizerListState.searchString, @@ -247,26 +265,31 @@ export const IndexDataVisualizerView: FC = (dataVi }; } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [currentSavedSearch, currentIndexPattern, dataVisualizerListState]); - - const setSearchParams = (searchParams: { - searchQuery: Query['query']; - searchString: Query['query']; - queryLanguage: SearchQueryLanguage; - }) => { - // When the user loads saved search and then clear or modify the query - // we should remove the saved search and replace it with the index pattern id - if (currentSavedSearch !== null) { - setCurrentSavedSearch(null); - } + }, [currentSavedSearch, currentIndexPattern, dataVisualizerListState, data.query]); + + const setSearchParams = useCallback( + (searchParams: { + searchQuery: Query['query']; + searchString: Query['query']; + queryLanguage: SearchQueryLanguage; + filters: Filter[]; + }) => { + // When the user loads saved search and then clear or modify the query + // we should remove the saved search and replace it with the index pattern id + if (currentSavedSearch !== null) { + setCurrentSavedSearch(null); + } - setDataVisualizerListState({ - ...dataVisualizerListState, - searchQuery: searchParams.searchQuery, - searchString: searchParams.searchString, - searchQueryLanguage: searchParams.queryLanguage, - }); - }; + setDataVisualizerListState({ + ...dataVisualizerListState, + searchQuery: searchParams.searchQuery, + searchString: searchParams.searchString, + searchQueryLanguage: searchParams.queryLanguage, + filters: searchParams.filters, + }); + }, + [currentSavedSearch, dataVisualizerListState, setDataVisualizerListState] + ); const samplerShardSize = dataVisualizerListState.samplerShardSize ?? restorableDefaults.samplerShardSize; @@ -305,6 +328,52 @@ export const IndexDataVisualizerView: FC = (dataVi const [nonMetricConfigs, setNonMetricConfigs] = useState(defaults.nonMetricConfigs); const [nonMetricsLoaded, setNonMetricsLoaded] = useState(defaults.nonMetricsLoaded); + const onAddFilter = useCallback( + (field: IndexPatternField | string, values: string, operation: '+' | '-') => { + const newFilters = generateFilters( + data.query.filterManager, + field, + values, + operation, + String(currentIndexPattern.id) + ); + if (newFilters) { + data.query.filterManager.addFilters(newFilters); + } + + // Merge current query with new filters + const mergedQuery = { + query: searchString || '', + language: searchQueryLanguage, + }; + + const combinedQuery = createMergedEsQuery( + { + query: searchString || '', + language: searchQueryLanguage, + }, + data.query.filterManager.getFilters() ?? [], + currentIndexPattern, + uiSettings + ); + + setSearchParams({ + searchQuery: combinedQuery, + searchString: mergedQuery.query, + queryLanguage: mergedQuery.language as SearchQueryLanguage, + filters: data.query.filterManager.getFilters(), + }); + }, + [ + currentIndexPattern, + data.query.filterManager, + searchQueryLanguage, + searchString, + setSearchParams, + uiSettings, + ] + ); + useEffect(() => { const timeUpdateSubscription = merge( timefilter.getTimeUpdate$(), @@ -666,11 +735,11 @@ export const IndexDataVisualizerView: FC = (dataVi const fieldData = nonMetricFieldData.find((f) => f.fieldName === field.spec.name); const nonMetricConfig = { - ...fieldData, + ...(fieldData ? fieldData : {}), fieldFormat: currentIndexPattern.getFormatterForField(field), aggregatable: field.aggregatable, scripted: field.scripted, - loading: fieldData.existsInDocs, + loading: fieldData?.existsInDocs, deletable: field.runtimeField !== undefined, }; @@ -751,13 +820,14 @@ export const IndexDataVisualizerView: FC = (dataVi item={item} indexPattern={currentIndexPattern} combinedQuery={{ searchQueryLanguage, searchString }} + onAddFilter={onAddFilter} /> ); } return m; }, {} as ItemIdToExpandedRowMap); }, - [currentIndexPattern, searchQueryLanguage, searchString] + [currentIndexPattern, searchQueryLanguage, searchString, onAddFilter] ); // Some actions open up fly-out or popup @@ -809,17 +879,10 @@ export const IndexDataVisualizerView: FC = (dataVi - + -
- +
+

{currentIndexPattern.title}

= (dataVi
- - - {currentIndexPattern.timeFieldName !== undefined && ( - - - - )} + + {currentIndexPattern.timeFieldName !== undefined && ( - + - - + )} + + + + @@ -862,8 +928,6 @@ export const IndexDataVisualizerView: FC = (dataVi /> )} - - = (dataVi visibleFieldNames={visibleFieldNames} setVisibleFieldNames={setVisibleFieldNames} showEmptyFields={showEmptyFields} + onAddFilter={onAddFilter} /> - + = ({ indexedFieldTypes, setVisibleFieldTypes, visibleFieldTypes }) => { const options: Option[] = useMemo(() => { return indexedFieldTypes.map((indexedFieldName) => { - const item = JOB_FIELD_TYPES_OPTIONS[indexedFieldName]; + const label = jobTypeLabels[indexedFieldName] ?? ''; return { value: indexedFieldName, name: ( - {item.name} + {label} {indexedFieldName && ( - + )} diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.scss b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.scss new file mode 100644 index 0000000000000..6f274921d5ebf --- /dev/null +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.scss @@ -0,0 +1,20 @@ +.dvSearchPanel__controls { + flex-direction: row; + padding: $euiSizeS; +} + +@include euiBreakpoint('xs', 's', 'm', 'l') { + .dvSearchPanel__container { + flex-direction: column; + } + .dvSearchBar { + min-width: #{'max(100%, 500px)'}; + } + .dvSearchPanel__controls { + padding: 0; + } + // prevent margin -16 which scrunches the filter bar + .globalFilterGroup__wrapper-isVisible { + margin: 0 !important; + } +} diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.tsx index 91ec1e449bb38..f55114ca36d78 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.tsx @@ -6,21 +6,22 @@ */ import React, { FC, useEffect, useState } from 'react'; -import { EuiCode, EuiFlexItem, EuiFlexGroup, EuiInputPopover } from '@elastic/eui'; +import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { Query, fromKueryExpression, luceneStringToDsl, toElasticsearchQuery } from '@kbn/es-query'; -import { QueryStringInput } from '../../../../../../../../src/plugins/data/public'; +import { Query, Filter } from '@kbn/es-query'; import { ShardSizeFilter } from './shard_size_select'; import { DataVisualizerFieldNamesFilter } from './field_name_filter'; -import { DatavisualizerFieldTypeFilter } from './field_type_filter'; -import { IndexPattern } from '../../../../../../../../src/plugins/data/common'; -import { JobFieldType } from '../../../../../common/types'; +import { DataVisualizerFieldTypeFilter } from './field_type_filter'; import { - ErrorMessage, - SEARCH_QUERY_LANGUAGE, - SearchQueryLanguage, -} from '../../types/combined_query'; - + IndexPattern, + IndexPatternField, + TimeRange, +} from '../../../../../../../../src/plugins/data/common'; +import { JobFieldType } from '../../../../../common/types'; +import { SearchQueryLanguage } from '../../types/combined_query'; +import { useDataVisualizerKibana } from '../../../kibana_context'; +import './_index.scss'; +import { createMergedEsQuery } from '../../utils/saved_search_utils'; interface Props { indexPattern: IndexPattern; searchString: Query['query']; @@ -38,12 +39,15 @@ interface Props { searchQuery, searchString, queryLanguage, + filters, }: { searchQuery: Query['query']; searchString: Query['query']; queryLanguage: SearchQueryLanguage; + filters: Filter[]; }): void; showEmptyFields: boolean; + onAddFilter?: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; } export const SearchPanel: FC = ({ @@ -61,98 +65,109 @@ export const SearchPanel: FC = ({ setSearchParams, showEmptyFields, }) => { + const { + services: { + uiSettings, + notifications: { toasts }, + data: { + query: queryManager, + ui: { SearchBar }, + }, + }, + } = useDataVisualizerKibana(); // The internal state of the input query bar updated on every key stroke. const [searchInput, setSearchInput] = useState({ query: searchString || '', language: searchQueryLanguage, }); - const [errorMessage, setErrorMessage] = useState(undefined); useEffect(() => { setSearchInput({ query: searchString || '', language: searchQueryLanguage, }); - }, [searchQueryLanguage, searchString]); + }, [searchQueryLanguage, searchString, queryManager.filterManager]); - const searchHandler = (query: Query) => { - let filterQuery; + const searchHandler = ({ query, filters }: { query?: Query; filters?: Filter[] }) => { + const mergedQuery = query ?? searchInput; + const mergedFilters = filters ?? queryManager.filterManager.getFilters(); try { - if (query.language === SEARCH_QUERY_LANGUAGE.KUERY) { - filterQuery = toElasticsearchQuery(fromKueryExpression(query.query), indexPattern); - } else if (query.language === SEARCH_QUERY_LANGUAGE.LUCENE) { - filterQuery = luceneStringToDsl(query.query); - } else { - filterQuery = {}; + if (mergedFilters) { + queryManager.filterManager.setFilters(mergedFilters); } + + const combinedQuery = createMergedEsQuery( + mergedQuery, + queryManager.filterManager.getFilters() ?? [], + indexPattern, + uiSettings + ); + setSearchParams({ - searchQuery: filterQuery, - searchString: query.query, - queryLanguage: query.language as SearchQueryLanguage, + searchQuery: combinedQuery, + searchString: mergedQuery.query, + queryLanguage: mergedQuery.language as SearchQueryLanguage, + filters: mergedFilters, }); } catch (e) { console.log('Invalid syntax', JSON.stringify(e, null, 2)); // eslint-disable-line no-console - setErrorMessage({ query: query.query as string, message: e.message }); + toasts.addError(e, { + title: i18n.translate('xpack.dataVisualizer.searchPanel.invalidSyntax', { + defaultMessage: 'Invalid syntax', + }), + }); } }; - const searchChangeHandler = (query: Query) => setSearchInput(query); return ( - - - setErrorMessage(undefined)} - input={ - + + + + searchHandler({ query: params.query }) } - isOpen={errorMessage?.query === searchInput.query && errorMessage?.message !== ''} - > - - {i18n.translate( - 'xpack.dataVisualizer.searchPanel.invalidKuerySyntaxErrorMessageQueryBar', - { - defaultMessage: 'Invalid query', - } - )} - {': '} - {errorMessage?.message.split('\n')[0]} - - + // @ts-expect-error onFiltersUpdated is a valid prop on SearchBar + onFiltersUpdated={(filters: Filter[]) => searchHandler({ filters })} + indexPatterns={[indexPattern]} + placeholder={i18n.translate('xpack.dataVisualizer.searchPanel.queryBarPlaceholderText', { + defaultMessage: 'Search… (e.g. status:200 AND extension:"PHP")', + })} + displayStyle={'inPage'} + isClearable={true} + customSubmitButton={
} + /> - + + + + - - ); }; diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/index_data_visualizer.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/index_data_visualizer.tsx index f9748da51a22d..c3f3d744a3978 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/index_data_visualizer.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/index_data_visualizer.tsx @@ -49,6 +49,7 @@ export const DataVisualizerUrlStateContextProvider: FC( undefined @@ -56,7 +57,6 @@ export const DataVisualizerUrlStateContextProvider: FC | null>( null ); - const { search: searchString } = useLocation(); useEffect(() => { const prevSearchString = searchString; diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/locator/index.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/locator/index.ts new file mode 100644 index 0000000000000..fb3e0100bbf75 --- /dev/null +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/locator/index.ts @@ -0,0 +1,8 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './locator'; diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/locator/locator.test.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/locator/locator.test.ts new file mode 100644 index 0000000000000..c8762aa79bbd5 --- /dev/null +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/locator/locator.test.ts @@ -0,0 +1,105 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IndexDataVisualizerLocatorDefinition } from './locator'; + +describe('Index data visualizer locator', () => { + const definition = new IndexDataVisualizerLocatorDefinition(); + + it('should generate valid URL for the Index Data Visualizer Viewer page with global settings', async () => { + const location = await definition.getLocation({ + indexPatternId: '3da93760-e0af-11ea-9ad3-3bcfc330e42a', + timeRange: { + from: 'now-30m', + to: 'now', + }, + refreshInterval: { pause: false, value: 300 }, + }); + + expect(location).toMatchObject({ + app: 'ml', + path: '/jobs/new_job/datavisualizer?index=3da93760-e0af-11ea-9ad3-3bcfc330e42a&_a=(DATA_VISUALIZER_INDEX_VIEWER:())&_g=(refreshInterval:(pause:!f,value:300),time:(from:now-30m,to:now))', + state: {}, + }); + }); + + it('should prioritize savedSearchId even when index pattern id is available', async () => { + const location = await definition.getLocation({ + indexPatternId: '3da93760-e0af-11ea-9ad3-3bcfc330e42a', + savedSearchId: '45014020-dffa-11eb-b120-a105fbbe93b3', + }); + + expect(location).toMatchObject({ + app: 'ml', + path: '/jobs/new_job/datavisualizer?savedSearchId=45014020-dffa-11eb-b120-a105fbbe93b3&_a=(DATA_VISUALIZER_INDEX_VIEWER:())&_g=()', + state: {}, + }); + }); + + it('should generate valid URL with field names and field types', async () => { + const location = await definition.getLocation({ + indexPatternId: '3da93760-e0af-11ea-9ad3-3bcfc330e42a', + visibleFieldNames: ['@timestamp', 'responsetime'], + visibleFieldTypes: ['number'], + }); + + expect(location).toMatchObject({ + app: 'ml', + path: "/jobs/new_job/datavisualizer?index=3da93760-e0af-11ea-9ad3-3bcfc330e42a&_a=(DATA_VISUALIZER_INDEX_VIEWER:(visibleFieldNames:!('@timestamp',responsetime),visibleFieldTypes:!(number)))&_g=()", + }); + }); + + it('should generate valid URL with KQL query', async () => { + const location = await definition.getLocation({ + indexPatternId: '3da93760-e0af-11ea-9ad3-3bcfc330e42a', + query: { + searchQuery: { + bool: { + should: [ + { + match: { + region: 'ap-northwest-1', + }, + }, + ], + minimum_should_match: 1, + }, + }, + searchString: 'region : ap-northwest-1', + searchQueryLanguage: 'kuery', + }, + }); + + expect(location).toMatchObject({ + app: 'ml', + path: "/jobs/new_job/datavisualizer?index=3da93760-e0af-11ea-9ad3-3bcfc330e42a&_a=(DATA_VISUALIZER_INDEX_VIEWER:(searchQuery:(bool:(minimum_should_match:1,should:!((match:(region:ap-northwest-1))))),searchQueryLanguage:kuery,searchString:'region : ap-northwest-1'))&_g=()", + state: {}, + }); + }); + + it('should generate valid URL with Lucene query', async () => { + const location = await definition.getLocation({ + indexPatternId: '3da93760-e0af-11ea-9ad3-3bcfc330e42a', + query: { + searchQuery: { + query_string: { + query: 'region: ap-northwest-1', + analyze_wildcard: true, + }, + }, + searchString: 'region : ap-northwest-1', + searchQueryLanguage: 'lucene', + }, + }); + + expect(location).toMatchObject({ + app: 'ml', + path: "/jobs/new_job/datavisualizer?index=3da93760-e0af-11ea-9ad3-3bcfc330e42a&_a=(DATA_VISUALIZER_INDEX_VIEWER:(searchQuery:(query_string:(analyze_wildcard:!t,query:'region: ap-northwest-1')),searchQueryLanguage:lucene,searchString:'region : ap-northwest-1'))&_g=()", + state: {}, + }); + }); +}); diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/locator/locator.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/locator/locator.ts new file mode 100644 index 0000000000000..c26a668bd04ab --- /dev/null +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/locator/locator.ts @@ -0,0 +1,131 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// @ts-ignore +import { encode } from 'rison-node'; +import { stringify } from 'query-string'; +import { SerializableRecord } from '@kbn/utility-types'; +import { RefreshInterval, TimeRange } from '../../../../../../../src/plugins/data/common'; +import { LocatorDefinition, LocatorPublic } from '../../../../../../../src/plugins/share/common'; +import { QueryState } from '../../../../../../../src/plugins/data/public'; +import { Dictionary, isRisonSerializationRequired } from '../../common/util/url_state'; +import { SearchQueryLanguage } from '../types/combined_query'; + +export const DATA_VISUALIZER_APP_LOCATOR = 'DATA_VISUALIZER_APP_LOCATOR'; + +export interface IndexDataVisualizerLocatorParams extends SerializableRecord { + /** + * Optionally set saved search ID. + */ + savedSearchId?: string; + + /** + * Optionally set index pattern ID. + */ + indexPatternId?: string; + + /** + * Optionally set the time range in the time picker. + */ + timeRange?: TimeRange; + + /** + * Optionally set the refresh interval. + */ + refreshInterval?: RefreshInterval & SerializableRecord; + + /** + * Optionally set a query. + */ + query?: { + searchQuery: SerializableRecord; + searchString: string | SerializableRecord; + searchQueryLanguage: SearchQueryLanguage; + }; + + /** + * If not given, will use the uiSettings configuration for `storeInSessionStorage`. useHash determines + * whether to hash the data in the url to avoid url length issues. + */ + useHash?: boolean; + /** + * Optionally set visible field names. + */ + visibleFieldNames?: string[]; + /** + * Optionally set visible field types. + */ + visibleFieldTypes?: string[]; +} + +export type IndexDataVisualizerLocator = LocatorPublic; + +export class IndexDataVisualizerLocatorDefinition + implements LocatorDefinition +{ + public readonly id = DATA_VISUALIZER_APP_LOCATOR; + + constructor() {} + + public readonly getLocation = async (params: IndexDataVisualizerLocatorParams) => { + const { + indexPatternId, + query, + refreshInterval, + savedSearchId, + timeRange, + visibleFieldNames, + visibleFieldTypes, + } = params; + + const appState: { + searchQuery?: { [key: string]: any }; + searchQueryLanguage?: string; + searchString?: string | SerializableRecord; + visibleFieldNames?: string[]; + visibleFieldTypes?: string[]; + } = {}; + const queryState: QueryState = {}; + + if (query) { + appState.searchQuery = query.searchQuery; + appState.searchString = query.searchString; + appState.searchQueryLanguage = query.searchQueryLanguage; + } + if (visibleFieldNames) appState.visibleFieldNames = visibleFieldNames; + if (visibleFieldTypes) appState.visibleFieldTypes = visibleFieldTypes; + + if (timeRange) queryState.time = timeRange; + if (refreshInterval) queryState.refreshInterval = refreshInterval; + + const urlState: Dictionary = { + ...(savedSearchId ? { savedSearchId } : { index: indexPatternId }), + _a: { DATA_VISUALIZER_INDEX_VIEWER: appState }, + _g: queryState, + }; + + const parsedQueryString: Dictionary = {}; + Object.keys(urlState).forEach((a) => { + if (isRisonSerializationRequired(a)) { + parsedQueryString[a] = encode(urlState[a]); + } else { + parsedQueryString[a] = urlState[a]; + } + }); + const newLocationSearchString = stringify(parsedQueryString, { + sort: false, + encode: false, + }); + + const path = `/jobs/new_job/datavisualizer?${newLocationSearchString}`; + return { + app: 'ml', + path, + state: {}, + }; + }; +} diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/types/index_data_visualizer_state.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/types/index_data_visualizer_state.ts index 7cd1c2bb3ce09..81c5a9097e0ef 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/types/index_data_visualizer_state.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/types/index_data_visualizer_state.ts @@ -5,6 +5,7 @@ * 2.0. */ +import type { Filter } from '@kbn/es-query'; import { Query } from '../../../../../../../src/plugins/data/common/query'; import { SearchQueryLanguage } from './combined_query'; @@ -25,4 +26,5 @@ export interface DataVisualizerIndexBasedAppState extends Omit50","language":"lucene"},"filter":[{"meta":{"index":"90a978e0-1c80-11ec-b1d7-f7e5cf21b9e0","negate":false,"disabled":false,"alias":null,"type":"phrase","key":"airline","value":"ASA","params":{"query":"ASA","type":"phrase"}},"query":{"match":{"airline":{"query":"ASA","type":"phrase"}}},"$state":{"store":"appState"}}],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}', + }, + }, + id: '93fc4d60-1c80-11ec-b1d7-f7e5cf21b9e0', + type: 'search', +}; + +// @ts-expect-error We don't need the full object here +const luceneInvalidSavedSearchObj: SavedSearchSavedObject = { + attributes: { + kibanaSavedObjectMeta: { + searchSourceJSON: null, + }, + }, + id: '93fc4d60-1c80-11ec-b1d7-f7e5cf21b9e0', + type: 'search', +}; + +const kqlSavedSearch: SavedSearch = { + title: 'farequote_filter_and_kuery', + description: '', + columns: ['_source'], + // @ts-expect-error We don't need the full object here + kibanaSavedObjectMeta: { + searchSourceJSON: + '{"highlightAll":true,"version":true,"query":{"query":"responsetime > 49","language":"kuery"},"filter":[{"meta":{"index":"90a978e0-1c80-11ec-b1d7-f7e5cf21b9e0","negate":false,"disabled":false,"alias":null,"type":"phrase","key":"airline","value":"ASA","params":{"query":"ASA","type":"phrase"}},"query":{"match":{"airline":{"query":"ASA","type":"phrase"}}},"$state":{"store":"appState"}}],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}', + }, +}; + +describe('getQueryFromSavedSearch()', () => { + it('should return parsed searchSourceJSON with query and filter', () => { + expect(getQueryFromSavedSearch(luceneSavedSearchObj)).toEqual({ + filter: [ + { + $state: { store: 'appState' }, + meta: { + alias: null, + disabled: false, + index: '90a978e0-1c80-11ec-b1d7-f7e5cf21b9e0', + key: 'airline', + negate: false, + params: { query: 'ASA', type: 'phrase' }, + type: 'phrase', + value: 'ASA', + }, + query: { match: { airline: { query: 'ASA', type: 'phrase' } } }, + }, + ], + highlightAll: true, + indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index', + query: { language: 'lucene', query: 'responsetime:>50' }, + version: true, + }); + expect(getQueryFromSavedSearch(kqlSavedSearch)).toEqual({ + filter: [ + { + $state: { store: 'appState' }, + meta: { + alias: null, + disabled: false, + index: '90a978e0-1c80-11ec-b1d7-f7e5cf21b9e0', + key: 'airline', + negate: false, + params: { query: 'ASA', type: 'phrase' }, + type: 'phrase', + value: 'ASA', + }, + query: { match: { airline: { query: 'ASA', type: 'phrase' } } }, + }, + ], + highlightAll: true, + indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index', + query: { language: 'kuery', query: 'responsetime > 49' }, + version: true, + }); + }); + it('should return undefined if invalid searchSourceJSON', () => { + expect(getQueryFromSavedSearch(luceneInvalidSavedSearchObj)).toEqual(undefined); + }); +}); + +describe('createMergedEsQuery()', () => { + const luceneQuery = { + query: 'responsetime:>50', + language: 'lucene', + }; + const kqlQuery = { + query: 'responsetime > 49', + language: 'kuery', + }; + const mockFilters: Filter[] = [ + { + meta: { + index: '90a978e0-1c80-11ec-b1d7-f7e5cf21b9e0', + negate: false, + disabled: false, + alias: null, + type: 'phrase', + key: 'airline', + params: { + query: 'ASA', + }, + }, + query: { + match: { + airline: { + query: 'ASA', + type: 'phrase', + }, + }, + }, + $state: { + store: 'appState' as FilterStateStore, + }, + }, + ]; + + it('return formatted ES bool query with both the original query and filters combined', () => { + expect(createMergedEsQuery(luceneQuery, mockFilters)).toEqual({ + bool: { + filter: [{ match_phrase: { airline: { query: 'ASA' } } }], + must: [{ query_string: { query: 'responsetime:>50' } }], + must_not: [], + should: [], + }, + }); + expect(createMergedEsQuery(kqlQuery, mockFilters)).toEqual({ + bool: { + filter: [{ match_phrase: { airline: { query: 'ASA' } } }], + minimum_should_match: 1, + must_not: [], + should: [{ range: { responsetime: { gt: '49' } } }], + }, + }); + }); + it('return formatted ES bool query without filters ', () => { + expect(createMergedEsQuery(luceneQuery)).toEqual({ + bool: { + filter: [], + must: [{ query_string: { query: 'responsetime:>50' } }], + must_not: [], + should: [], + }, + }); + expect(createMergedEsQuery(kqlQuery)).toEqual({ + bool: { + filter: [], + minimum_should_match: 1, + must_not: [], + should: [{ range: { responsetime: { gt: '49' } } }], + }, + }); + }); +}); + +describe('getEsQueryFromSavedSearch()', () => { + it('return undefined if saved search is not provided', () => { + expect( + getEsQueryFromSavedSearch({ + indexPattern: mockDataView, + savedSearch: undefined, + uiSettings: mockUiSettings, + }) + ).toEqual(undefined); + }); + it('return search data from saved search if neither query nor filter is provided ', () => { + expect( + getEsQueryFromSavedSearch({ + indexPattern: mockDataView, + savedSearch: luceneSavedSearchObj, + uiSettings: mockUiSettings, + }) + ).toEqual({ + queryLanguage: 'lucene', + searchQuery: { + bool: { + filter: [{ match_phrase: { airline: { query: 'ASA' } } }], + must: [{ query_string: { query: 'responsetime:>50' } }], + must_not: [], + should: [], + }, + }, + searchString: 'responsetime:>50', + }); + }); + it('should override original saved search with the provided query ', () => { + expect( + getEsQueryFromSavedSearch({ + indexPattern: mockDataView, + savedSearch: luceneSavedSearchObj, + uiSettings: mockUiSettings, + query: { + query: 'responsetime:>100', + language: 'lucene', + }, + }) + ).toEqual({ + queryLanguage: 'lucene', + searchQuery: { + bool: { + filter: [{ match_phrase: { airline: { query: 'ASA' } } }], + must: [{ query_string: { query: 'responsetime:>100' } }], + must_not: [], + should: [], + }, + }, + searchString: 'responsetime:>100', + }); + }); + + it('should override original saved search with the provided filters ', () => { + expect( + getEsQueryFromSavedSearch({ + indexPattern: mockDataView, + savedSearch: luceneSavedSearchObj, + uiSettings: mockUiSettings, + query: { + query: 'responsetime:>100', + language: 'lucene', + }, + filters: [ + { + meta: { + index: '90a978e0-1c80-11ec-b1d7-f7e5cf21b9e0', + alias: null, + negate: true, + disabled: false, + type: 'phrase', + key: 'airline', + params: { + query: 'JZA', + }, + }, + query: { + match_phrase: { + airline: 'JZA', + }, + }, + $state: { + store: 'appState' as FilterStateStore, + }, + }, + ], + }) + ).toEqual({ + queryLanguage: 'lucene', + searchQuery: { + bool: { + filter: [], + must: [{ query_string: { query: 'responsetime:>100' } }], + must_not: [{ match_phrase: { airline: 'JZA' } }], + should: [], + }, + }, + searchString: 'responsetime:>100', + }); + }); +}); diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.ts index cb80e491fc7e5..80a2069aab1a8 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.ts @@ -8,55 +8,155 @@ import { cloneDeep } from 'lodash'; import { IUiSettingsClient } from 'kibana/public'; import { - buildEsQuery, - buildQueryFromFilters, - decorateQuery, fromKueryExpression, - luceneStringToDsl, toElasticsearchQuery, + buildQueryFromFilters, + buildEsQuery, + Query, + Filter, } from '@kbn/es-query'; -import { estypes } from '@elastic/elasticsearch'; -import { SavedSearchSavedObject } from '../../../../common/types'; +import { isSavedSearchSavedObject, SavedSearchSavedObject } from '../../../../common/types'; import { IndexPattern } from '../../../../../../../src/plugins/data/common'; import { SEARCH_QUERY_LANGUAGE, SearchQueryLanguage } from '../types/combined_query'; -import { getEsQueryConfig, Query } from '../../../../../../../src/plugins/data/public'; - -export function getQueryFromSavedSearch(savedSearch: SavedSearchSavedObject) { - const search = savedSearch.attributes.kibanaSavedObjectMeta as { searchSourceJSON: string }; - return JSON.parse(search.searchSourceJSON) as { - query: Query; - filter: any[]; - }; +import { SavedSearch } from '../../../../../../../src/plugins/discover/public'; +import { getEsQueryConfig } from '../../../../../../../src/plugins/data/common'; +import { FilterManager } from '../../../../../../../src/plugins/data/public'; + +/** + * Parse the stringified searchSourceJSON + * from a saved search or saved search object + */ +export function getQueryFromSavedSearch(savedSearch: SavedSearchSavedObject | SavedSearch) { + const search = isSavedSearchSavedObject(savedSearch) + ? savedSearch?.attributes?.kibanaSavedObjectMeta + : // @ts-expect-error kibanaSavedObjectMeta does exist + savedSearch?.kibanaSavedObjectMeta; + + const parsed = + typeof search?.searchSourceJSON === 'string' + ? (JSON.parse(search.searchSourceJSON) as { + query: Query; + filter: Filter[]; + }) + : undefined; + + // Remove indexRefName because saved search might no longer be relevant + // if user modifies the query or filter + // after opening a saved search + if (parsed && Array.isArray(parsed.filter)) { + parsed.filter.forEach((f) => { + // @ts-expect-error indexRefName does appear in meta for newly created saved search + f.meta.indexRefName = undefined; + }); + } + return parsed; } /** - * Extract query data from the saved search object. + * Create an Elasticsearch query that combines both lucene/kql query string and filters + * Should also form a valid query if only the query or filters is provided */ -export function extractSearchData( - savedSearch: SavedSearchSavedObject | null, - currentIndexPattern: IndexPattern, - queryStringOptions: Record | string +export function createMergedEsQuery( + query?: Query, + filters?: Filter[], + indexPattern?: IndexPattern, + uiSettings?: IUiSettingsClient ) { - if (!savedSearch) { - return undefined; - } + let combinedQuery: any = getDefaultQuery(); + + if (query && query.language === SEARCH_QUERY_LANGUAGE.KUERY) { + const ast = fromKueryExpression(query.query); + if (query.query !== '') { + combinedQuery = toElasticsearchQuery(ast, indexPattern); + } + const filterQuery = buildQueryFromFilters(filters, indexPattern); + + if (Array.isArray(combinedQuery.bool.filter) === false) { + combinedQuery.bool.filter = + combinedQuery.bool.filter === undefined ? [] : [combinedQuery.bool.filter]; + } + + if (Array.isArray(combinedQuery.bool.must_not) === false) { + combinedQuery.bool.must_not = + combinedQuery.bool.must_not === undefined ? [] : [combinedQuery.bool.must_not]; + } - const { query: extractedQuery } = getQueryFromSavedSearch(savedSearch); - const queryLanguage = extractedQuery.language as SearchQueryLanguage; - const qryString = extractedQuery.query; - let qry; - if (queryLanguage === SEARCH_QUERY_LANGUAGE.KUERY) { - const ast = fromKueryExpression(qryString); - qry = toElasticsearchQuery(ast, currentIndexPattern); + combinedQuery.bool.filter = [...combinedQuery.bool.filter, ...filterQuery.filter]; + combinedQuery.bool.must_not = [...combinedQuery.bool.must_not, ...filterQuery.must_not]; } else { - qry = luceneStringToDsl(qryString); - decorateQuery(qry, queryStringOptions); + combinedQuery = buildEsQuery( + indexPattern, + query ? [query] : [], + filters ? filters : [], + uiSettings ? getEsQueryConfig(uiSettings) : undefined + ); + } + return combinedQuery; +} + +/** + * Extract query data from the saved search object + * with overrides from the provided query data and/or filters + */ +export function getEsQueryFromSavedSearch({ + indexPattern, + uiSettings, + savedSearch, + query, + filters, + filterManager, +}: { + indexPattern: IndexPattern; + uiSettings: IUiSettingsClient; + savedSearch: SavedSearchSavedObject | SavedSearch | null | undefined; + query?: Query; + filters?: Filter[]; + filterManager?: FilterManager; +}) { + if (!indexPattern || !savedSearch) return; + + const savedSearchData = getQueryFromSavedSearch(savedSearch); + const userQuery = query; + const userFilters = filters; + + // If no saved search available, use user's query and filters + if (!savedSearchData && userQuery) { + if (filterManager && userFilters) filterManager.setFilters(userFilters); + + const combinedQuery = createMergedEsQuery( + userQuery, + Array.isArray(userFilters) ? userFilters : [], + indexPattern, + uiSettings + ); + + return { + searchQuery: combinedQuery, + searchString: userQuery.query, + queryLanguage: userQuery.language as SearchQueryLanguage, + }; + } + + // If saved search available, merge saved search with latest user query or filters differ from extracted saved search data + if (savedSearchData) { + const currentQuery = userQuery ?? savedSearchData?.query; + const currentFilters = userFilters ?? savedSearchData?.filter; + + if (filterManager) filterManager.setFilters(currentFilters); + + const combinedQuery = createMergedEsQuery( + currentQuery, + Array.isArray(currentFilters) ? currentFilters : [], + indexPattern, + uiSettings + ); + + return { + searchQuery: combinedQuery, + searchString: currentQuery.query, + queryLanguage: currentQuery.language as SearchQueryLanguage, + }; } - return { - searchQuery: qry, - searchString: qryString, - queryLanguage, - }; } const DEFAULT_QUERY = { @@ -69,64 +169,6 @@ const DEFAULT_QUERY = { }, }; -export function getDefaultDatafeedQuery() { +export function getDefaultQuery() { return cloneDeep(DEFAULT_QUERY); } - -export function createSearchItems( - kibanaConfig: IUiSettingsClient, - indexPattern: IndexPattern | undefined, - savedSearch: SavedSearchSavedObject | null -) { - // query is only used by the data visualizer as it needs - // a lucene query_string. - // Using a blank query will cause match_all:{} to be used - // when passed through luceneStringToDsl - let query: Query = { - query: '', - language: 'lucene', - }; - - let combinedQuery: estypes.QueryDslQueryContainer = getDefaultDatafeedQuery(); - if (savedSearch !== null) { - const data = getQueryFromSavedSearch(savedSearch); - - query = data.query; - const filter = data.filter; - - const filters = Array.isArray(filter) ? filter : []; - - if (query.language === SEARCH_QUERY_LANGUAGE.KUERY) { - const ast = fromKueryExpression(query.query); - if (query.query !== '') { - combinedQuery = toElasticsearchQuery(ast, indexPattern); - } - const filterQuery = buildQueryFromFilters(filters, indexPattern); - - if (!combinedQuery.bool) { - throw new Error('Missing bool on query'); - } - - if (!Array.isArray(combinedQuery.bool.filter)) { - combinedQuery.bool.filter = - combinedQuery.bool.filter === undefined ? [] : [combinedQuery.bool.filter]; - } - - if (!Array.isArray(combinedQuery.bool.must_not)) { - combinedQuery.bool.must_not = - combinedQuery.bool.must_not === undefined ? [] : [combinedQuery.bool.must_not]; - } - - combinedQuery.bool.filter = [...combinedQuery.bool.filter, ...filterQuery.filter]; - combinedQuery.bool.must_not = [...combinedQuery.bool.must_not, ...filterQuery.must_not]; - } else { - const esQueryConfigs = getEsQueryConfig(kibanaConfig); - combinedQuery = buildEsQuery(indexPattern, [query], filters, esQueryConfigs); - } - } - - return { - query, - combinedQuery, - }; -} diff --git a/x-pack/plugins/data_visualizer/public/plugin.ts b/x-pack/plugins/data_visualizer/public/plugin.ts index 54f27a2e9d72e..112294f4b246f 100644 --- a/x-pack/plugins/data_visualizer/public/plugin.ts +++ b/x-pack/plugins/data_visualizer/public/plugin.ts @@ -48,7 +48,10 @@ export class DataVisualizerPlugin DataVisualizerStartDependencies > { - public setup(core: CoreSetup, plugins: DataVisualizerSetupDependencies) { + public setup( + core: CoreSetup, + plugins: DataVisualizerSetupDependencies + ) { if (plugins.home) { registerHomeAddData(plugins.home); registerHomeFeatureCatalogue(plugins.home); diff --git a/x-pack/plugins/ml/public/application/routing/routes/new_job/index_or_search.tsx b/x-pack/plugins/ml/public/application/routing/routes/new_job/index_or_search.tsx index 5d903bd865911..8500d85d5580a 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/new_job/index_or_search.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/new_job/index_or_search.tsx @@ -83,6 +83,7 @@ const PageWrapper: FC = ({ nextStepPath, deps, mode }) = application: { navigateToUrl }, }, } = useMlKibana(); + const { redirectToMlAccessDeniedPage } = deps; const redirectToJobsManagementPage = useCreateAndNavigateToMlLink( ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE diff --git a/x-pack/plugins/ml/public/locator/ml_locator.test.ts b/x-pack/plugins/ml/public/locator/ml_locator.test.ts index 3b736a9af4e3e..cac31abd9f62a 100644 --- a/x-pack/plugins/ml/public/locator/ml_locator.test.ts +++ b/x-pack/plugins/ml/public/locator/ml_locator.test.ts @@ -9,7 +9,7 @@ import { MlLocatorDefinition } from './ml_locator'; import { ML_PAGES } from '../../common/constants/locator'; import { ANALYSIS_CONFIG_TYPE } from '../../common/constants/data_frame_analytics'; -describe('MlUrlGenerator', () => { +describe('ML locator', () => { const definition = new MlLocatorDefinition(); describe('AnomalyDetection', () => { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 1ae338dead5a7..4e8ef7ceaf93a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -9172,7 +9172,6 @@ "xpack.dataVisualizer.removeCombinedFieldsLabel": "結合されたフィールドを削除", "xpack.dataVisualizer.searchPanel.allFieldsLabel": "すべてのフィールド", "xpack.dataVisualizer.searchPanel.allOptionLabel": "すべて検索", - "xpack.dataVisualizer.searchPanel.invalidKuerySyntaxErrorMessageQueryBar": "無効なクエリ", "xpack.dataVisualizer.searchPanel.numberFieldsLabel": "数値フィールド", "xpack.dataVisualizer.searchPanel.ofFieldsTotal": "合計 {totalCount}", "xpack.dataVisualizer.searchPanel.queryBarPlaceholder": "小さいサンプルサイズを選択することで、クエリの実行時間を短縮しクラスターへの負荷を軽減できます。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 34ec05212f67e..6d9a75e3264a4 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -9260,7 +9260,6 @@ "xpack.dataVisualizer.removeCombinedFieldsLabel": "移除组合字段", "xpack.dataVisualizer.searchPanel.allFieldsLabel": "所有字段", "xpack.dataVisualizer.searchPanel.allOptionLabel": "搜索全部", - "xpack.dataVisualizer.searchPanel.invalidKuerySyntaxErrorMessageQueryBar": "无效查询", "xpack.dataVisualizer.searchPanel.numberFieldsLabel": "字段数目", "xpack.dataVisualizer.searchPanel.ofFieldsTotal": ",共 {totalCount} 个", "xpack.dataVisualizer.searchPanel.queryBarPlaceholder": "选择较小的样例大小将减少查询运行时间和集群上的负载。", diff --git a/x-pack/test/functional/services/ml/data_visualizer_table.ts b/x-pack/test/functional/services/ml/data_visualizer_table.ts index 2f67a9b75e3d6..8094f0ad1f8d2 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_table.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_table.ts @@ -110,11 +110,11 @@ export function MachineLearningDataVisualizerTableProvider( if (!(await testSubjects.exists(this.detailsSelector(fieldName)))) { const selector = this.rowSelector( fieldName, - `dataVisualizerDetailsToggle-${fieldName}-arrowDown` + `dataVisualizerDetailsToggle-${fieldName}-arrowRight` ); await testSubjects.click(selector); await testSubjects.existOrFail( - this.rowSelector(fieldName, `dataVisualizerDetailsToggle-${fieldName}-arrowUp`), + this.rowSelector(fieldName, `dataVisualizerDetailsToggle-${fieldName}-arrowDown`), { timeout: 1000, } @@ -128,10 +128,10 @@ export function MachineLearningDataVisualizerTableProvider( await retry.tryForTime(10000, async () => { if (await testSubjects.exists(this.detailsSelector(fieldName))) { await testSubjects.click( - this.rowSelector(fieldName, `dataVisualizerDetailsToggle-${fieldName}-arrowUp`) + this.rowSelector(fieldName, `dataVisualizerDetailsToggle-${fieldName}-arrowDown`) ); await testSubjects.existOrFail( - this.rowSelector(fieldName, `dataVisualizerDetailsToggle-${fieldName}-arrowDown`), + this.rowSelector(fieldName, `dataVisualizerDetailsToggle-${fieldName}-arrowRight`), { timeout: 1000, } @@ -150,7 +150,7 @@ export function MachineLearningDataVisualizerTableProvider( const docCount = await testSubjects.getVisibleText(docCountFormattedSelector); expect(docCount).to.eql( docCountFormatted, - `Expected field document count to be '${docCountFormatted}' (got '${docCount}')` + `Expected field ${fieldName}'s document count to be '${docCountFormatted}' (got '${docCount}')` ); } From fc9fc0d68784c54a02d71f26052595d98527ff5d Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 20:43:08 -0400 Subject: [PATCH 25/51] [vscode] Set typescript.enablePromptUseWorkspaceTsdk (#113476) (#113578) To prompt users to use the workspace configured version of Typescript. Signed-off-by: Tyler Smalley Co-authored-by: Tyler Smalley --- .../kbn-dev-utils/src/vscode_config/managed_config_keys.ts | 6 +++++- .../kbn-dev-utils/src/vscode_config/update_vscode_config.ts | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/kbn-dev-utils/src/vscode_config/managed_config_keys.ts b/packages/kbn-dev-utils/src/vscode_config/managed_config_keys.ts index 8b941e9e4f71f..07156ca9257fb 100644 --- a/packages/kbn-dev-utils/src/vscode_config/managed_config_keys.ts +++ b/packages/kbn-dev-utils/src/vscode_config/managed_config_keys.ts @@ -8,7 +8,7 @@ export interface ManagedConfigKey { key: string; - value: string | Record; + value: string | Record | boolean; } /** @@ -42,4 +42,8 @@ export const MANAGED_CONFIG_KEYS: ManagedConfigKey[] = [ // we use a relative path here so that it works with remote vscode connections value: './node_modules/typescript/lib', }, + { + key: 'typescript.enablePromptUseWorkspaceTsdk', + value: true, + }, ]; diff --git a/packages/kbn-dev-utils/src/vscode_config/update_vscode_config.ts b/packages/kbn-dev-utils/src/vscode_config/update_vscode_config.ts index 42a642ef1b6c4..b337c8baa5e32 100644 --- a/packages/kbn-dev-utils/src/vscode_config/update_vscode_config.ts +++ b/packages/kbn-dev-utils/src/vscode_config/update_vscode_config.ts @@ -69,10 +69,10 @@ const createObjectPropOfManagedValues = (key: string, value: Record const addManagedProp = ( ast: t.ObjectExpression, key: string, - value: string | Record + value: string | Record | boolean ) => { ast.properties.push( - typeof value === 'string' + typeof value === 'string' || typeof value === 'boolean' ? createManagedProp(key, value) : createObjectPropOfManagedValues(key, value) ); @@ -89,7 +89,7 @@ const addManagedProp = ( const replaceManagedProp = ( ast: t.ObjectExpression, existing: BasicObjectProp, - value: string | Record + value: string | Record | boolean ) => { remove(ast.properties, existing); addManagedProp(ast, existing.key.value, value); From 2ec7d50c6d4250ea3db7631e6b4fb08caf8c1efa Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 20:55:17 -0400 Subject: [PATCH 26/51] [Lens] Design updates in prep for thresholds (#113428) (#113579) * remove borders from layer panels * increase "add layer" button size * fix cross-browser scrollbars & shadow clipping * fix layer panel and dimension drop zone spacing * add hyphens * fix border radius & adjust workspace styles * config flyout padding and spacing adjustments * formula padding and spacing adjustments * palette flyout padding and spacing adjustments * remove unnecessary v7 styles * correct button font size in v7 * restore workspace border in v7 * Fix draggable bg color in v8 dark mode * fix misaligned layer chart picker text * fix unit test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Michael Marcialis --- x-pack/plugins/lens/public/_mixins.scss | 2 +- .../editor_frame/config_panel/add_layer.tsx | 2 - .../buttons/empty_dimension_button.tsx | 4 +- .../config_panel/config_panel.scss | 10 -- .../config_panel/config_panel.tsx | 2 - .../config_panel/dimension_container.scss | 40 +---- .../config_panel/dimension_container.tsx | 15 +- .../config_panel/layer_panel.scss | 44 ++--- .../config_panel/layer_panel.test.tsx | 2 +- .../editor_frame/config_panel/layer_panel.tsx | 167 +++++++++--------- .../editor_frame/frame_layout.scss | 7 +- .../workspace_panel_wrapper.scss | 29 +-- .../workspace_panel_wrapper.tsx | 5 + .../indexpattern_datasource/datapanel.scss | 1 - .../dimension_panel/dimension_editor.scss | 13 +- .../dimension_panel/dimension_editor.tsx | 4 +- .../definitions/formula/editor/formula.scss | 8 +- .../formula/editor/formula_editor.tsx | 2 + .../formula/editor/formula_help.tsx | 41 +++-- .../coloring/palette_configuration.scss | 9 +- .../coloring/palette_panel_container.scss | 38 +--- .../coloring/palette_panel_container.tsx | 14 +- 22 files changed, 195 insertions(+), 264 deletions(-) delete mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.scss diff --git a/x-pack/plugins/lens/public/_mixins.scss b/x-pack/plugins/lens/public/_mixins.scss index 5a798bcc6c23b..7282de214636c 100644 --- a/x-pack/plugins/lens/public/_mixins.scss +++ b/x-pack/plugins/lens/public/_mixins.scss @@ -15,7 +15,7 @@ // Static styles for a draggable item @mixin lnsDraggable { @include euiSlightShadow; - background: lightOrDarkTheme($euiColorEmptyShade, $euiColorLightestShade); + background: $euiColorEmptyShade; border: $euiBorderWidthThin dashed transparent; cursor: grab; } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx index 69e4aa629cec6..e052e06f1b2f1 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx @@ -64,7 +64,6 @@ export function AddLayerButton({
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.scss deleted file mode 100644 index 0d51108fb2dcb..0000000000000 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.scss +++ /dev/null @@ -1,10 +0,0 @@ -.lnsConfigPanel__addLayerBtn { - @include kbnThemeStyle('v7') { - // sass-lint:disable-block no-important - background-color: transparent !important; - color: transparentize($euiColorMediumShade, .3) !important; - border-color: $euiColorLightShade !important; - box-shadow: none !important; - } - -} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index 57e4cf5b8dffd..0b6223ac87ce2 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -5,8 +5,6 @@ * 2.0. */ -import './config_panel.scss'; - import React, { useMemo, memo } from 'react'; import { EuiForm } from '@elastic/eui'; import { mapValues } from 'lodash'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.scss index 135286fc2172b..692db8171d124 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.scss @@ -24,48 +24,24 @@ } } -.lnsDimensionContainer__footer { - padding: $euiSizeS; - - .lnsFrameLayout__sidebar-isFullscreen & { - display: none; - } -} - .lnsDimensionContainer__header { - padding: $euiSizeS $euiSizeXS; + padding: $euiSize; .lnsFrameLayout__sidebar-isFullscreen & { display: none; } } -.lnsDimensionContainer__headerTitle { - padding: $euiSizeS $euiSizeXS; - cursor: pointer; - - &:hover { - text-decoration: underline; - } +.lnsDimensionContainer__content { + @include euiYScroll; + flex: 1; } -.lnsDimensionContainer__headerLink { - &:focus-within { - background-color: transparentize($euiColorVis1, .9); - - .lnsDimensionContainer__headerTitle { - text-decoration: underline; - } - } -} - -.lnsDimensionContainer__backIcon { - &:hover { - transform: none !important; // sass-lint:disable-line no-important - } +.lnsDimensionContainer__footer { + padding: $euiSize; - &:focus { - background-color: transparent; + .lnsFrameLayout__sidebar-isFullscreen & { + display: none; } } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx index c62b10093e6e5..f7402e78ebd96 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx @@ -98,13 +98,7 @@ export function DimensionContainer({ }} > - +

+ - - {panel} - + +
{panel}
+ {i18n.translate('xpack.lens.dimensionContainer.close', { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss index 7a1cbb8237f50..781a08d0f60bb 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss @@ -36,32 +36,32 @@ background: $euiColorLightestShade; padding: $euiSize; + &:last-child { + border-radius: 0 0 $euiBorderRadius $euiBorderRadius; + } + // Add border to the top of the next same panel & + & { border-top: $euiBorderThin; margin-top: 0; } - &:last-child { - border-bottom-right-radius: $euiBorderRadius; - border-bottom-left-radius: $euiBorderRadius; + + & > * { + margin-bottom: 0; } -} -.lnsLayerPanel__row--notSupportsMoreColumns { - padding-bottom: 0; + // Targeting EUI class as we are unable to apply a class to this element in component + &, + .euiFormRow__fieldWrapper { + & > * + * { + margin-top: $euiSize; + } + } } .lnsLayerPanel__group { - padding: $euiSizeS 0; - margin-bottom: $euiSizeS; -} - -.lnsLayerPanel__group:empty { - padding: 0; -} - -.lnsLayerPanel__error { - padding: 0 $euiSizeS; + margin: (-$euiSizeS) (-$euiSize); + padding: $euiSizeS $euiSize; } .lnsLayerPanel__dimension { @@ -87,11 +87,10 @@ } .lnsLayerPanel__dimensionContainer { - margin: 0 0 $euiSizeS; position: relative; - &:last-child { - margin-bottom: 0; + & + & { + margin-top: $euiSizeS; } } @@ -105,6 +104,10 @@ min-height: $euiSizeXXL - 2; word-break: break-word; font-weight: $euiFontWeightRegular; + + @include kbnThemeStyle('v7') { + font-size: $euiFontSizeS; + } } .lnsLayerPanel__triggerTextLabel { @@ -119,7 +122,8 @@ } .lnsLayerPanel__styleEditor { - padding: 0 $euiSizeS $euiSizeS; + margin-top: -$euiSizeS; + padding: 0 $euiSize $euiSize; } .lnsLayerPanel__colorIndicator { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx index f777fd0976dfd..5b432f85efde2 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx @@ -222,7 +222,7 @@ describe('LayerPanel', () => { const group = instance .find(EuiFormRow) - .findWhere((e) => e.prop('error')?.props?.children === 'Required dimension'); + .findWhere((e) => e.prop('error') === 'Required dimension'); expect(group).toHaveLength(1); }); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 6e2e77af4f3b0..8d19620cebbdc 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -326,12 +326,7 @@ export function LayerPanel( className="lnsLayerPanel" style={{ visibility: isDimensionPanelOpen ? 'hidden' : 'visible' }} > - +
@@ -394,16 +389,13 @@ export function LayerPanel( {groups.map((group, groupIndex) => { const isMissing = !isEmptyLayer && group.required && group.accessors.length === 0; + return ( + <> {group.groupLabel} {group.groupTooltip && ( <> @@ -420,92 +412,91 @@ export function LayerPanel( /> )} -

+ } labelType="legend" key={group.groupId} isInvalid={isMissing} error={ - isMissing ? ( -
- {i18n.translate('xpack.lens.editorFrame.requiredDimensionWarningLabel', { + isMissing + ? i18n.translate('xpack.lens.editorFrame.requiredDimensionWarningLabel', { defaultMessage: 'Required dimension', - })} -
- ) : ( - [] - ) + }) + : [] } > <> - - {group.accessors.map((accessorConfig, accessorIndex) => { - const { columnId } = accessorConfig; - - return ( - -
- { - setActiveDimension({ - isNew: false, - activeGroup: group, - activeId: id, - }); - }} - onRemoveClick={(id: string) => { - trackUiEvent('indexpattern_dimension_removed'); - props.updateAll( - datasourceId, - layerDatasource.removeColumn({ - layerId, - columnId: id, - prevState: layerDatasourceState, - }), - activeVisualization.removeDimension({ - layerId, - columnId: id, - prevState: props.visualizationState, - frame: framePublicAPI, - }) - ); - removeButtonRef(id); - }} - > - + {group.accessors.map((accessorConfig, accessorIndex) => { + const { columnId } = accessorConfig; + + return ( + +
+ { + setActiveDimension({ + isNew: false, + activeGroup: group, + activeId: id, + }); }} - /> - -
-
- ); - })} - + onRemoveClick={(id: string) => { + trackUiEvent('indexpattern_dimension_removed'); + props.updateAll( + datasourceId, + layerDatasource.removeColumn({ + layerId, + columnId: id, + prevState: layerDatasourceState, + }), + activeVisualization.removeDimension({ + layerId, + columnId: id, + prevState: props.visualizationState, + frame: framePublicAPI, + }) + ); + removeButtonRef(id); + }} + > + +
+
+
+ ); + })} +
+ ) : null} + {group.supportsMoreColumns ? ( * { flex: 1 1 100%; @@ -38,10 +42,13 @@ } .lnsWorkspacePanel__dragDrop { - width: 100%; - height: 100%; - border: $euiBorderThin; - border-radius: $euiBorderRadiusSmall; + @include kbnThemeStyle('v7') { + border: $euiBorderThin; + } + + @include kbnThemeStyle('v8') { + border: $euiBorderWidthThin solid transparent; + } &.lnsDragDrop-isDropTarget { @include lnsDroppable; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx index e1cb1aeb9f825..d8959e714d16e 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx @@ -125,10 +125,15 @@ export function WorkspacePanelWrapper({
+ {children} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss index 0303e6549d8df..c93f05c6eb19a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss +++ b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss @@ -54,6 +54,5 @@ .lnsLayerPanelChartSwitch_title { font-weight: 600; display: inline; - vertical-align: middle; padding-left: 8px; } \ No newline at end of file diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.scss b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.scss index 874291ae25e34..30e2e00c7c85d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.scss +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.scss @@ -6,8 +6,8 @@ position: sticky; top: 0; background: $euiColorEmptyShade; - // Raise it above the elements that are after it in DOM order - z-index: $euiZLevel1; + z-index: $euiZLevel1; // Raise it above the elements that are after it in DOM order + padding: 0 $euiSize; } .lnsIndexPatternDimensionEditor-isFullscreen { @@ -23,21 +23,14 @@ } .lnsIndexPatternDimensionEditor__section--padded { - padding: $euiSizeS; + padding: $euiSize; } .lnsIndexPatternDimensionEditor__section--shaded { background-color: $euiColorLightestShade; -} - -.lnsIndexPatternDimensionEditor__section--top { border-bottom: $euiBorderThin; } -.lnsIndexPatternDimensionEditor__section--bottom { - border-top: $euiBorderThin; -} - .lnsIndexPatternDimensionEditor__columns { column-count: 2; column-gap: $euiSizeXL; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index 333e76f5a4f57..29bbe6a96b9e1 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -422,7 +422,7 @@ export function DimensionEditor(props: DimensionEditorProps) { maxWidth={false} />
- +
{!incompleteInfo && selectedColumn && @@ -636,8 +636,6 @@ export function DimensionEditor(props: DimensionEditorProps) { /> )}
- - ); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula.scss b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula.scss index d66e19bec8a1c..92a778ebfb803 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula.scss +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula.scss @@ -17,8 +17,6 @@ } .lnsFormula__editor { - border-bottom: $euiBorderThin; - .lnsIndexPatternDimensionEditor-isFullscreen & { border-bottom: none; display: flex; @@ -32,7 +30,7 @@ .lnsFormula__editorHeader, .lnsFormula__editorFooter { - padding: $euiSizeS; + padding: $euiSizeS $euiSize; } .lnsFormula__editorFooter { @@ -130,7 +128,7 @@ } .lnsFormula__docsSearch { - padding: $euiSizeS; + padding: $euiSize; } .lnsFormula__docsNav { @@ -138,7 +136,7 @@ } .lnsFormula__docsNavGroup { - padding: $euiSizeS; + padding: $euiSize; & + & { border-top: $euiBorderThin; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx index 4f6f13ea843ef..7abe80003ea0e 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx @@ -549,6 +549,8 @@ export function FormulaEditor({ dimension: { width: 320, height: 200 }, fixedOverflowWidgets: true, matchBrackets: 'always', + // Undocumented Monaco option to force left margin width + lineDecorationsWidth: 16, }, }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_help.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_help.tsx index 3c3068a595bc0..47dd8fbc9c569 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_help.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_help.tsx @@ -18,6 +18,7 @@ import { EuiTitle, EuiFieldSearch, EuiHighlight, + EuiSpacer, } from '@elastic/eui'; import { Markdown } from '../../../../../../../../../src/plugins/kibana_react/public'; import { IndexPattern } from '../../../../types'; @@ -298,7 +299,7 @@ sum(products.base_price) / overall_sum(sum(products.base_price)) return ( <> - + {i18n.translate('xpack.lens.formulaDocumentation.header', { defaultMessage: 'Formula reference', })} @@ -347,22 +348,28 @@ sum(products.base_price) / overall_sum(sum(products.base_price)) - - {helpGroup.items.map((helpItem) => { - return ( - {helpItem.label} - } - size="s" - onClick={() => { - setSelectedFunction(helpItem.label); - }} - /> - ); - })} - + {helpGroup.items.length ? ( + <> + + + + {helpGroup.items.map((helpItem) => { + return ( + {helpItem.label} + } + size="s" + onClick={() => { + setSelectedFunction(helpItem.label); + }} + /> + ); + })} + + + ) : null} ); })} diff --git a/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.scss b/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.scss index c6b14c5c5f9a3..82165b172eab9 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.scss +++ b/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.scss @@ -1,7 +1,8 @@ -.lnsPalettePanel__section--shaded { - background-color: $euiColorLightestShade; +.lnsPalettePanel__section { + padding: $euiSize; } -.lnsPalettePanel__section { - padding: $euiSizeS; +.lnsPalettePanel__section--shaded { + background-color: $euiColorLightestShade; + border-bottom: $euiBorderThin; } \ No newline at end of file diff --git a/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.scss b/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.scss index db14d064d1881..edf6c472321bf 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.scss +++ b/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.scss @@ -15,39 +15,15 @@ z-index: $euiZLevel3 + 1 } -.lnsPalettePanelContainer__footer { - padding: $euiSizeS; -} - .lnsPalettePanelContainer__header { - padding: $euiSizeS $euiSizeXS; -} - -.lnsPalettePanelContainer__headerTitle { - padding: $euiSizeS $euiSizeXS; - cursor: pointer; - - &:hover { - text-decoration: underline; - } + padding: $euiSize; } -.lnsPalettePanelContainer__headerLink { - &:focus-within { - background-color: transparentize($euiColorVis1, .9); - - .lnsPalettePanelContainer__headerTitle { - text-decoration: underline; - } - } +.lnsPalettePanelContainer__content { + @include euiYScroll; + flex: 1; } -.lnsPalettePanelContainer__backIcon { - &:hover { - transform: none !important; // sass-lint:disable-line no-important - } - - &:focus { - background-color: transparent; - } -} +.lnsPalettePanelContainer__footer { + padding: $euiSize; +} \ No newline at end of file diff --git a/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx b/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx index 583d6e25ed4e2..b546ffe5fb6fb 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx @@ -59,12 +59,7 @@ export function PalettePanelContainer({ className="lnsPalettePanelContainer" > - + +

- - {children} - + +
{children}
+ {i18n.translate('xpack.lens.table.palettePanelContainer.back', { From 6ba24fc82add26d42074fc4be5fd9a7dc83266c2 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 20:59:17 -0400 Subject: [PATCH 27/51] [buildkite] Improve failed test experience (#113483) (#113580) Co-authored-by: Brian Seeders --- .buildkite/pipelines/es_snapshots/verify.yml | 8 - .buildkite/pipelines/hourly.yml | 8 - .../lifecycle/annotate_test_failures.js | 14 ++ .buildkite/scripts/lifecycle/post_command.sh | 5 + packages/kbn-test/BUILD.bazel | 2 + .../report_failures_to_file.ts | 138 ++++++++++++++++++ ...report_failures_to_file_html_template.html | 41 ++++++ .../run_failed_tests_reporter_cli.ts | 3 + 8 files changed, 203 insertions(+), 16 deletions(-) create mode 100644 .buildkite/scripts/lifecycle/annotate_test_failures.js create mode 100644 packages/kbn-test/src/failed_tests_reporter/report_failures_to_file.ts create mode 100644 packages/kbn-test/src/failed_tests_reporter/report_failures_to_file_html_template.html diff --git a/.buildkite/pipelines/es_snapshots/verify.yml b/.buildkite/pipelines/es_snapshots/verify.yml index 9af2e938db49d..61212e1fcf0a8 100755 --- a/.buildkite/pipelines/es_snapshots/verify.yml +++ b/.buildkite/pipelines/es_snapshots/verify.yml @@ -91,13 +91,5 @@ steps: - wait: ~ continue_on_failure: true - - plugins: - - junit-annotate#v1.9.0: - artifacts: target/junit/**/*.xml - job-uuid-file-pattern: '-bk__(.*).xml' - - - wait: ~ - continue_on_failure: true - - command: .buildkite/scripts/lifecycle/post_build.sh label: Post-Build diff --git a/.buildkite/pipelines/hourly.yml b/.buildkite/pipelines/hourly.yml index 89541023be8e2..279c8cf96bfe3 100644 --- a/.buildkite/pipelines/hourly.yml +++ b/.buildkite/pipelines/hourly.yml @@ -159,13 +159,5 @@ steps: - wait: ~ continue_on_failure: true - - plugins: - - junit-annotate#v1.9.0: - artifacts: target/junit/**/*.xml - job-uuid-file-pattern: '-bk__(.*).xml' - - - wait: ~ - continue_on_failure: true - - command: .buildkite/scripts/lifecycle/post_build.sh label: Post-Build diff --git a/.buildkite/scripts/lifecycle/annotate_test_failures.js b/.buildkite/scripts/lifecycle/annotate_test_failures.js new file mode 100644 index 0000000000000..caf1e08c2bb4d --- /dev/null +++ b/.buildkite/scripts/lifecycle/annotate_test_failures.js @@ -0,0 +1,14 @@ +const { TestFailures } = require('kibana-buildkite-library'); + +(async () => { + try { + await TestFailures.annotateTestFailures(); + } catch (ex) { + console.error('Annotate test failures error', ex.message); + if (ex.response) { + console.error('HTTP Error Response Status', ex.response.status); + console.error('HTTP Error Response Body', ex.response.data); + } + process.exit(1); + } +})(); diff --git a/.buildkite/scripts/lifecycle/post_command.sh b/.buildkite/scripts/lifecycle/post_command.sh index 23f44a586e978..0f98035f9f828 100755 --- a/.buildkite/scripts/lifecycle/post_command.sh +++ b/.buildkite/scripts/lifecycle/post_command.sh @@ -23,4 +23,9 @@ if [[ "$IS_TEST_EXECUTION_STEP" == "true" ]]; then buildkite-agent artifact upload '.es/**/*.hprof' node scripts/report_failed_tests --build-url="${BUILDKITE_BUILD_URL}#${BUILDKITE_JOB_ID}" 'target/junit/**/*.xml' + + if [[ -d 'target/test_failures' ]]; then + buildkite-agent artifact upload 'target/test_failures/**/*' + node .buildkite/scripts/lifecycle/annotate_test_failures.js + fi fi diff --git a/packages/kbn-test/BUILD.bazel b/packages/kbn-test/BUILD.bazel index 36e81df6d8c3c..efaf01f7137d9 100644 --- a/packages/kbn-test/BUILD.bazel +++ b/packages/kbn-test/BUILD.bazel @@ -50,6 +50,7 @@ RUNTIME_DEPS = [ "@npm//exit-hook", "@npm//form-data", "@npm//globby", + "@npm//he", "@npm//history", "@npm//jest", "@npm//jest-cli", @@ -85,6 +86,7 @@ TYPES_DEPS = [ "@npm//xmlbuilder", "@npm//@types/chance", "@npm//@types/enzyme", + "@npm//@types/he", "@npm//@types/history", "@npm//@types/jest", "@npm//@types/joi", diff --git a/packages/kbn-test/src/failed_tests_reporter/report_failures_to_file.ts b/packages/kbn-test/src/failed_tests_reporter/report_failures_to_file.ts new file mode 100644 index 0000000000000..aca2e6838faec --- /dev/null +++ b/packages/kbn-test/src/failed_tests_reporter/report_failures_to_file.ts @@ -0,0 +1,138 @@ +/* + * 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 { createHash } from 'crypto'; +import { mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'fs'; +import { join, basename, resolve } from 'path'; + +import { ToolingLog } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; +import { escape } from 'he'; + +import { TestFailure } from './get_failures'; + +const findScreenshots = (dirPath: string, allScreenshots: string[] = []) => { + const files = readdirSync(dirPath); + + for (const file of files) { + if (statSync(join(dirPath, file)).isDirectory()) { + if (file.match(/node_modules/)) { + continue; + } + + allScreenshots = findScreenshots(join(dirPath, file), allScreenshots); + } else { + const fullPath = join(dirPath, file); + if (fullPath.match(/screenshots\/failure\/.+\.png$/)) { + allScreenshots.push(fullPath); + } + } + } + + return allScreenshots; +}; + +export function reportFailuresToFile(log: ToolingLog, failures: TestFailure[]) { + if (!failures?.length) { + return; + } + + let screenshots: string[]; + try { + screenshots = [ + ...findScreenshots(join(REPO_ROOT, 'test', 'functional')), + ...findScreenshots(join(REPO_ROOT, 'x-pack', 'test', 'functional')), + ]; + } catch (e) { + log.error(e as Error); + screenshots = []; + } + + const screenshotsByName: Record = {}; + for (const screenshot of screenshots) { + const [name] = basename(screenshot).split('.'); + screenshotsByName[name] = screenshot; + } + + // Jest could, in theory, fail 1000s of tests and write 1000s of failures + // So let's just write files for the first 20 + for (const failure of failures.slice(0, 20)) { + const hash = createHash('md5').update(failure.name).digest('hex'); + const filenameBase = `${ + process.env.BUILDKITE_JOB_ID ? process.env.BUILDKITE_JOB_ID + '_' : '' + }${hash}`; + const dir = join('target', 'test_failures'); + + const failureLog = [ + ['Test:', '-----', failure.classname, failure.name, ''], + ['Failure:', '--------', failure.failure], + failure['system-out'] ? ['', 'Standard Out:', '-------------', failure['system-out']] : [], + ] + .flat() + .join('\n'); + + const failureJSON = JSON.stringify( + { + ...failure, + hash, + buildId: process.env.BUJILDKITE_BUILD_ID || '', + jobId: process.env.BUILDKITE_JOB_ID || '', + url: process.env.BUILDKITE_BUILD_URL || '', + jobName: process.env.BUILDKITE_LABEL + ? `${process.env.BUILDKITE_LABEL}${ + process.env.BUILDKITE_PARALLEL_JOB ? ` #${process.env.BUILDKITE_PARALLEL_JOB}` : '' + }` + : '', + }, + null, + 2 + ); + + let screenshot = ''; + const screenshotName = `${failure.name.replace(/([^ a-zA-Z0-9-]+)/g, '_')}`; + if (screenshotsByName[screenshotName]) { + try { + screenshot = readFileSync(screenshotsByName[screenshotName]).toString('base64'); + } catch (e) { + log.error(e as Error); + } + } + + const screenshotHtml = screenshot + ? `` + : ''; + + const failureHTML = readFileSync( + resolve( + REPO_ROOT, + 'packages/kbn-test/src/failed_tests_reporter/report_failures_to_file_html_template.html' + ) + ) + .toString() + .replace('$TITLE', escape(failure.name)) + .replace( + '$MAIN', + ` + ${failure.classname + .split('.') + .map((part) => `
${escape(part.replace('·', '.'))}
`) + .join('')} +
+

${escape(failure.name)}

+
${escape(failure.failure)}
+ ${screenshotHtml} +
${escape(failure['system-out'] || '')}
+ ` + ); + + mkdirSync(dir, { recursive: true }); + writeFileSync(join(dir, `${filenameBase}.log`), failureLog, 'utf8'); + writeFileSync(join(dir, `${filenameBase}.html`), failureHTML, 'utf8'); + writeFileSync(join(dir, `${filenameBase}.json`), failureJSON, 'utf8'); + } +} diff --git a/packages/kbn-test/src/failed_tests_reporter/report_failures_to_file_html_template.html b/packages/kbn-test/src/failed_tests_reporter/report_failures_to_file_html_template.html new file mode 100644 index 0000000000000..485a2c2d4eb3f --- /dev/null +++ b/packages/kbn-test/src/failed_tests_reporter/report_failures_to_file_html_template.html @@ -0,0 +1,41 @@ + + + + + + + + $TITLE + + +
+
$MAIN
+
+ + + diff --git a/packages/kbn-test/src/failed_tests_reporter/run_failed_tests_reporter_cli.ts b/packages/kbn-test/src/failed_tests_reporter/run_failed_tests_reporter_cli.ts index 0129614fe658d..6c88b7408b628 100644 --- a/packages/kbn-test/src/failed_tests_reporter/run_failed_tests_reporter_cli.ts +++ b/packages/kbn-test/src/failed_tests_reporter/run_failed_tests_reporter_cli.ts @@ -21,6 +21,7 @@ import { readTestReport } from './test_report'; import { addMessagesToReport } from './add_messages_to_report'; import { getReportMessageIter } from './report_metadata'; import { reportFailuresToEs } from './report_failures_to_es'; +import { reportFailuresToFile } from './report_failures_to_file'; const DEFAULT_PATTERNS = [Path.resolve(REPO_ROOT, 'target/junit/**/*.xml')]; @@ -98,6 +99,8 @@ export function runFailedTestsReporterCli() { const messages = Array.from(getReportMessageIter(report)); const failures = await getFailures(report); + reportFailuresToFile(log, failures); + if (indexInEs) { await reportFailuresToEs(log, failures); } From 538f332118e7114aadca897e5c74d069b25aa042 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 30 Sep 2021 21:14:04 -0400 Subject: [PATCH 28/51] Prevent spec file updates from restarting dev server (#113336) (#113584) Signed-off-by: Tyler Smalley Co-authored-by: Tyler Smalley --- packages/kbn-cli-dev-mode/src/get_server_watch_paths.test.ts | 2 +- packages/kbn-cli-dev-mode/src/get_server_watch_paths.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kbn-cli-dev-mode/src/get_server_watch_paths.test.ts b/packages/kbn-cli-dev-mode/src/get_server_watch_paths.test.ts index 1a49faabd8da3..c07fa423992d6 100644 --- a/packages/kbn-cli-dev-mode/src/get_server_watch_paths.test.ts +++ b/packages/kbn-cli-dev-mode/src/get_server_watch_paths.test.ts @@ -38,7 +38,7 @@ it('produces the right watch and ignore list', () => { expect(ignorePaths).toMatchInlineSnapshot(` Array [ /\\[\\\\\\\\\\\\/\\]\\(\\\\\\.\\.\\*\\|node_modules\\|bower_components\\|target\\|public\\|__\\[a-z0-9_\\]\\+__\\|coverage\\)\\(\\[\\\\\\\\\\\\/\\]\\|\\$\\)/, - /\\\\\\.test\\\\\\.\\(js\\|tsx\\?\\)\\$/, + /\\\\\\.\\(test\\|spec\\)\\\\\\.\\(js\\|ts\\|tsx\\)\\$/, /\\\\\\.\\(md\\|sh\\|txt\\)\\$/, /debug\\\\\\.log\\$/, /src/plugins/*/test/**, diff --git a/packages/kbn-cli-dev-mode/src/get_server_watch_paths.ts b/packages/kbn-cli-dev-mode/src/get_server_watch_paths.ts index f08e456940808..6a45c86d794bd 100644 --- a/packages/kbn-cli-dev-mode/src/get_server_watch_paths.ts +++ b/packages/kbn-cli-dev-mode/src/get_server_watch_paths.ts @@ -52,7 +52,7 @@ export function getServerWatchPaths({ pluginPaths, pluginScanDirs }: Options) { const ignorePaths = [ /[\\\/](\..*|node_modules|bower_components|target|public|__[a-z0-9_]+__|coverage)([\\\/]|$)/, - /\.test\.(js|tsx?)$/, + /\.(test|spec)\.(js|ts|tsx)$/, /\.(md|sh|txt)$/, /debug\.log$/, ...pluginInternalDirsIgnore, From 69946328bb6a8889fdca0f3195c3e77064e4a424 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 00:17:22 -0400 Subject: [PATCH 29/51] [CI] Balance CI Groups (#112836) (#112875) Co-authored-by: Brian Seeders --- .../tests/exception_operators_data_types/index.ts | 5 ----- x-pack/test/functional/apps/ml/index.ts | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/index.ts index 5adf31aef5a3e..cebd20b698c26 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/index.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/index.ts @@ -27,11 +27,6 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./keyword')); loadTestFile(require.resolve('./keyword_array')); loadTestFile(require.resolve('./long')); - }); - - describe('', function () { - this.tags('ciGroup13'); - loadTestFile(require.resolve('./text')); loadTestFile(require.resolve('./text_array')); }); diff --git a/x-pack/test/functional/apps/ml/index.ts b/x-pack/test/functional/apps/ml/index.ts index eaf626618726a..d4bf9a22367bf 100644 --- a/x-pack/test/functional/apps/ml/index.ts +++ b/x-pack/test/functional/apps/ml/index.ts @@ -53,7 +53,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { }); describe('', function () { - this.tags('ciGroup13'); + this.tags('ciGroup8'); before(async () => { await ml.securityCommon.createMlRoles(); From cf6c46477be263bc4c41dc744ce523dac51f353b Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 00:58:19 -0400 Subject: [PATCH 30/51] [docs] Fixes typo in Deb install docs (#113590) (#113591) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 魔王卷子 Co-authored-by: Tyler Smalley Co-authored-by: 魔王卷子 --- docs/setup/install/deb.asciidoc | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/setup/install/deb.asciidoc b/docs/setup/install/deb.asciidoc index f154bc47460b0..46b65565ee6b8 100644 --- a/docs/setup/install/deb.asciidoc +++ b/docs/setup/install/deb.asciidoc @@ -165,7 +165,6 @@ locations for a Debian-based system: | Configuration files including `kibana.yml` | /etc/kibana | <> - d| | data | The location of the data files written to disk by Kibana and its plugins From 8422a244b1fcddf91304956d410c83d3a16159a0 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 03:32:46 -0400 Subject: [PATCH 31/51] Lint git index content on commit (#113300) (#113594) This fixes two fundamental issues: * the list of files to be linted is built looking at the diffs of the ref specified with `--ref` (or otherwise the current index) with those on the filesystem * the content to be linted is read from the filesystem instead of the specified `ref` or the one in the index Co-authored-by: Domenico Andreoli --- src/dev/eslint/lint_files.ts | 46 ++++++++++- src/dev/file.ts | 18 +++++ .../precommit_hook/get_files_for_commit.js | 81 +++++++++++++++---- src/dev/run_precommit_hook.js | 13 ++- src/dev/stylelint/lint_files.js | 56 ++++++++++--- 5 files changed, 183 insertions(+), 31 deletions(-) diff --git a/src/dev/eslint/lint_files.ts b/src/dev/eslint/lint_files.ts index 5c6118edeb2ec..b5849c0f8485f 100644 --- a/src/dev/eslint/lint_files.ts +++ b/src/dev/eslint/lint_files.ts @@ -12,6 +12,45 @@ import { REPO_ROOT } from '@kbn/utils'; import { createFailError, ToolingLog } from '@kbn/dev-utils'; import { File } from '../file'; +// For files living on the filesystem +function lintFilesOnFS(cli: CLIEngine, files: File[]) { + const paths = files.map((file) => file.getRelativePath()); + return cli.executeOnFiles(paths); +} + +// For files living somewhere else (ie. git object) +async function lintFilesOnContent(cli: CLIEngine, files: File[]) { + const report: { + results: any[]; + errorCount: number; + warningCount: number; + fixableErrorCount: number; + fixableWarningCount: number; + } = { + results: [], + errorCount: 0, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + }; + + for (let i = 0; i < files.length; i++) { + const r = cli.executeOnText(await files[i].getContent(), files[i].getRelativePath()); + // Despite a relative path was given, the result would contain an absolute one. Work around it. + r.results[0].filePath = r.results[0].filePath.replace( + files[i].getAbsolutePath(), + files[i].getRelativePath() + ); + report.results.push(...r.results); + report.errorCount += r.errorCount; + report.warningCount += r.warningCount; + report.fixableErrorCount += r.fixableErrorCount; + report.fixableWarningCount += r.fixableWarningCount; + } + + return report; +} + /** * Lints a list of files with eslint. eslint reports are written to the log * and a FailError is thrown when linting errors occur. @@ -20,15 +59,16 @@ import { File } from '../file'; * @param {Array} files * @return {undefined} */ -export function lintFiles(log: ToolingLog, files: File[], { fix }: { fix?: boolean } = {}) { +export async function lintFiles(log: ToolingLog, files: File[], { fix }: { fix?: boolean } = {}) { const cli = new CLIEngine({ cache: true, cwd: REPO_ROOT, fix, }); - const paths = files.map((file) => file.getRelativePath()); - const report = cli.executeOnFiles(paths); + const virtualFilesCount = files.filter((file) => file.isVirtual()).length; + const report = + virtualFilesCount && !fix ? await lintFilesOnContent(cli, files) : lintFilesOnFS(cli, files); if (fix) { CLIEngine.outputFixes(report); diff --git a/src/dev/file.ts b/src/dev/file.ts index b532a7bb70602..01005b257a403 100644 --- a/src/dev/file.ts +++ b/src/dev/file.ts @@ -7,11 +7,13 @@ */ import { dirname, extname, join, relative, resolve, sep, basename } from 'path'; +import { createFailError } from '@kbn/dev-utils'; export class File { private path: string; private relativePath: string; private ext: string; + private fileReader: undefined | (() => Promise); constructor(path: string) { this.path = resolve(path); @@ -55,6 +57,11 @@ export class File { ); } + // Virtual files cannot be read as usual, an helper is needed + public isVirtual() { + return this.fileReader !== undefined; + } + public getRelativeParentDirs() { const parents: string[] = []; @@ -81,4 +88,15 @@ export class File { public toJSON() { return this.relativePath; } + + public setFileReader(fileReader: () => Promise) { + this.fileReader = fileReader; + } + + public getContent() { + if (this.fileReader) { + return this.fileReader(); + } + throw createFailError('getContent() was invoked on a non-virtual File'); + } } diff --git a/src/dev/precommit_hook/get_files_for_commit.js b/src/dev/precommit_hook/get_files_for_commit.js index 44c8c9d5e6bc0..52dfab49c5c64 100644 --- a/src/dev/precommit_hook/get_files_for_commit.js +++ b/src/dev/precommit_hook/get_files_for_commit.js @@ -6,12 +6,65 @@ * Side Public License, v 1. */ +import { format } from 'util'; import SimpleGit from 'simple-git'; import { fromNode as fcb } from 'bluebird'; import { REPO_ROOT } from '@kbn/utils'; import { File } from '../file'; +/** + * Return the `git diff` argument used for building the list of files + * + * @param {String} gitRef + * @return {String} + * + * gitRef return + * '' '--cached' + * '' '~1..' + * '..' '..' + * '...' '...' + * '..' '..' + * '...' '...' + * '..' '..' + * '...' '...' + */ +function getRefForDiff(gitRef) { + if (!gitRef) { + return '--cached'; + } else if (gitRef.includes('..')) { + return gitRef; + } else { + return format('%s~1..%s', gitRef, gitRef); + } +} + +/** + * Return the used for reading files content + * + * @param {String} gitRef + * @return {String} + * + * gitRef return + * '' '' + * '' '' + * '..' 'HEAD' + * '...' 'HEAD' + * '..' '' + * '...' '' + * '..' '' + * '...' '' + */ +function getRefForCat(gitRef) { + if (!gitRef) { + return ''; + } else if (gitRef.includes('..')) { + return gitRef.endsWith('..') ? 'HEAD' : gitRef.slice(gitRef.lastIndexOf('..') + 2); + } else { + return gitRef; + } +} + /** * Get the files that are staged for commit (excluding deleted files) * as `File` objects that are aware of their commit status. @@ -21,29 +74,23 @@ import { File } from '../file'; */ export async function getFilesForCommit(gitRef) { const simpleGit = new SimpleGit(REPO_ROOT); - const gitRefForDiff = gitRef ? gitRef : '--cached'; - const output = await fcb((cb) => simpleGit.diff(['--name-status', gitRefForDiff], cb)); + const gitRefForDiff = getRefForDiff(gitRef); + const gitRefForCat = getRefForCat(gitRef); + + const output = await fcb((cb) => { + simpleGit.diff(['--diff-filter=d', '--name-only', gitRefForDiff], cb); + }); return ( output .split('\n') // Ignore blank lines .filter((line) => line.trim().length > 0) - // git diff --name-status outputs lines with two OR three parts - // separated by a tab character - .map((line) => line.trim().split('\t')) - .map(([status, ...paths]) => { - // ignore deleted files - if (status === 'D') { - return undefined; - } - - // the status is always in the first column - // .. If the file is edited the line will only have two columns - // .. If the file is renamed it will have three columns - // .. In any case, the last column is the CURRENT path to the file - return new File(paths[paths.length - 1]); + .map((path) => { + const file = new File(path); + const object = format('%s:%s', gitRefForCat, path); + file.setFileReader(() => fcb((cb) => simpleGit.catFile(['-p', object], cb))); + return file; }) - .filter(Boolean) ); } diff --git a/src/dev/run_precommit_hook.js b/src/dev/run_precommit_hook.js index 73394a62e3396..e1eafaf28d95d 100644 --- a/src/dev/run_precommit_hook.js +++ b/src/dev/run_precommit_hook.js @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { run, combineErrors, createFlagError } from '@kbn/dev-utils'; +import { run, combineErrors, createFlagError, createFailError } from '@kbn/dev-utils'; import * as Eslint from './eslint'; import * as Stylelint from './stylelint'; import { getFilesForCommit, checkFileCasing } from './precommit_hook'; @@ -23,6 +23,11 @@ run( throw createFlagError('expected --max-files to be a number greater than 0'); } + const virtualFilesCount = files.filter((file) => file.isVirtual()).length; + if (virtualFilesCount > 0 && virtualFilesCount < files.length) { + throw createFailError('Mixing of virtual and on-filesystem files is unsupported'); + } + if (maxFilesCount && files.length > maxFilesCount) { log.warning( `--max-files is set to ${maxFilesCount} and ${files.length} were discovered. The current script execution will be skipped.` @@ -66,7 +71,11 @@ run( help: ` --fix Execute eslint in --fix mode --max-files Max files number to check against. If exceeded the script will skip the execution - --ref Run checks against any git ref files (example HEAD or ) instead of running against staged ones + --ref Run checks against git ref files instead of running against staged ones + Examples: + HEAD~1..HEAD files changed in the commit at HEAD + HEAD equivalent to HEAD~1..HEAD + main... files changed in current branch since the common ancestor with main `, }, } diff --git a/src/dev/stylelint/lint_files.js b/src/dev/stylelint/lint_files.js index 6e62c85d44ae8..1ebc981728814 100644 --- a/src/dev/stylelint/lint_files.js +++ b/src/dev/stylelint/lint_files.js @@ -16,6 +16,51 @@ import { createFailError } from '@kbn/dev-utils'; const stylelintPath = path.resolve(__dirname, '..', '..', '..', '.stylelintrc'); const styleLintConfig = safeLoad(fs.readFileSync(stylelintPath)); +// For files living on the filesystem +function lintFilesOnFS(files) { + const paths = files.map((file) => file.getRelativePath()); + + const options = { + files: paths, + config: styleLintConfig, + formatter: 'string', + ignorePath: path.resolve(__dirname, '..', '..', '..', '.stylelintignore'), + }; + + return stylelint.lint(options); +} + +// For files living somewhere else (ie. git object) +async function lintFilesOnContent(files) { + const report = { + errored: false, + output: '', + postcssResults: [], + results: [], + maxWarningsExceeded: { + maxWarnings: 0, + foundWarnings: 0, + }, + }; + + for (let i = 0; i < files.length; i++) { + const options = { + code: await files[i].getContent(), + config: styleLintConfig, + formatter: 'string', + ignorePath: path.resolve(__dirname, '..', '..', '..', '.stylelintignore'), + }; + const r = await stylelint.lint(options); + report.errored = report.errored || r.errored; + report.output += r.output.replace(//, files[i].getRelativePath()).slice(0, -1); + report.postcssResults.push(...(r.postcssResults || [])); + report.maxWarnings = r.maxWarnings; + report.foundWarnings += r.foundWarnings; + } + + return report; +} + /** * Lints a list of files with eslint. eslint reports are written to the log * and a FailError is thrown when linting errors occur. @@ -25,16 +70,9 @@ const styleLintConfig = safeLoad(fs.readFileSync(stylelintPath)); * @return {undefined} */ export async function lintFiles(log, files) { - const paths = files.map((file) => file.getRelativePath()); - - const options = { - files: paths, - config: styleLintConfig, - formatter: 'string', - ignorePath: path.resolve(__dirname, '..', '..', '..', '.stylelintignore'), - }; + const virtualFilesCount = files.filter((file) => file.isVirtual()).length; + const report = virtualFilesCount ? await lintFilesOnContent(files) : await lintFilesOnFS(files); - const report = await stylelint.lint(options); if (report.errored) { log.error(report.output); throw createFailError('[stylelint] errors'); From cab1b559449cdee5083d6a57d30b84320d4ce1a6 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 06:45:50 -0400 Subject: [PATCH 32/51] [Lens] Threshold: set default color for new thresholds (#113008) (#113598) * :lipstick: Make dark grey default threshold color * :white_check_mark: Fix test * :ok_hand: Integrate feedback * :ok_hand: Fix bug * :ok_hand: Filter threshold layers for color assignments * :ok_hand: Small refactor * :bug: Fix merging conflicts Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Marco Liberati --- .../xy_visualization/color_assignment.ts | 31 +++++++++++----- .../public/xy_visualization/expression.tsx | 1 - .../expression_thresholds.tsx | 35 ++++--------------- .../public/xy_visualization/to_expression.ts | 7 ++-- .../xy_visualization/visualization.test.ts | 3 -- .../public/xy_visualization/visualization.tsx | 5 --- .../xy_config_panel/color_picker.tsx | 11 ++++-- 7 files changed, 41 insertions(+), 52 deletions(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts index 1e00d821d9b30..30238507c3566 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts @@ -8,9 +8,10 @@ import { uniq, mapValues } from 'lodash'; import type { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; import type { Datatable } from 'src/plugins/expressions'; +import { euiLightVars } from '@kbn/ui-shared-deps-src/theme'; import type { AccessorConfig, FramePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; -import type { FormatFactory } from '../../common'; +import { FormatFactory, LayerType, layerTypes } from '../../common'; import type { XYLayerConfig } from '../../common/expressions'; const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; @@ -20,8 +21,11 @@ interface LayerColorConfig { splitAccessor?: string; accessors: string[]; layerId: string; + layerType: LayerType; } +export const defaultThresholdColor = euiLightVars.euiColorDarkShade; + export type ColorAssignments = Record< string, { @@ -37,13 +41,15 @@ export function getColorAssignments( ): ColorAssignments { const layersPerPalette: Record = {}; - layers.forEach((layer) => { - const palette = layer.palette?.name || 'default'; - if (!layersPerPalette[palette]) { - layersPerPalette[palette] = []; - } - layersPerPalette[palette].push(layer); - }); + layers + .filter(({ layerType }) => layerType === layerTypes.DATA) + .forEach((layer) => { + const palette = layer.palette?.name || 'default'; + if (!layersPerPalette[palette]) { + layersPerPalette[palette] = []; + } + layersPerPalette[palette].push(layer); + }); return mapValues(layersPerPalette, (paletteLayers) => { const seriesPerLayer = paletteLayers.map((layer, layerIndex) => { @@ -111,6 +117,13 @@ export function getAccessorColorConfig( triggerIcon: 'disabled', }; } + if (layer.layerType === layerTypes.THRESHOLD) { + return { + columnId: accessor as string, + triggerIcon: 'color', + color: currentYConfig?.color || defaultThresholdColor, + }; + } const columnToLabel = getColumnToLabelMap(layer, frame.datasourceLayers[layer.layerId]); const rank = colorAssignments[currentPalette.name].getRank( layer, @@ -133,7 +146,7 @@ export function getAccessorColorConfig( return { columnId: accessor as string, triggerIcon: customColor ? 'color' : 'disabled', - color: customColor ? customColor : undefined, + color: customColor ?? undefined, }; }); } diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index bc80b4569977c..c4315b7ccea85 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -836,7 +836,6 @@ export function XYChart({ ; paletteService: PaletteRegistry; syncColors: boolean; @@ -36,12 +34,11 @@ export const ThresholdAnnotations = ({ if (!thresholdLayer.yConfig) { return []; } - const { columnToLabel, palette, yConfig: yConfigs, layerId } = thresholdLayer; + const { columnToLabel, yConfig: yConfigs, layerId } = thresholdLayer; const columnToLabelMap: Record = columnToLabel ? JSON.parse(columnToLabel) : {}; const table = data.tables[layerId]; - const colorAssignment = colorAssignments[palette.name]; const row = table.rows[0]; @@ -62,27 +59,7 @@ export const ThresholdAnnotations = ({ const formatter = formatters[groupId || 'bottom']; - const seriesLayers: SeriesLayer[] = [ - { - name: columnToLabelMap[yConfig.forAccessor], - totalSeriesAtDepth: colorAssignment.totalSeriesCount, - rankAtDepth: colorAssignment.getRank( - thresholdLayer, - String(yConfig.forAccessor), - String(yConfig.forAccessor) - ), - }, - ]; - const defaultColor = paletteService.get(palette.name).getCategoricalColor( - seriesLayers, - { - maxDepth: 1, - behindText: false, - totalSeries: colorAssignment.totalSeriesCount, - syncColors, - }, - palette.params - ); + const defaultColor = euiLightVars.euiColorDarkShade; const props = { groupId, @@ -99,7 +76,7 @@ export const ThresholdAnnotations = ({ const sharedStyle = { strokeWidth: yConfig.lineWidth || 1, - stroke: (yConfig.color || defaultColor) ?? '#f00', + stroke: yConfig.color || defaultColor, dash: dashStyle, }; @@ -179,7 +156,7 @@ export const ThresholdAnnotations = ({ })} style={{ ...sharedStyle, - fill: (yConfig.color || defaultColor) ?? '#f00', + fill: yConfig.color || defaultColor, opacity: 0.1, }} /> diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index 1996f918b675e..b66f0ca4687b8 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -13,6 +13,7 @@ import { OperationMetadata, DatasourcePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; import type { ValidLayer, XYLayerConfig } from '../../common/expressions'; import { layerTypes } from '../../common'; +import { defaultThresholdColor } from './color_assignment'; export const getSortedAccessors = (datasource: DatasourcePublicAPI, layer: XYLayerConfig) => { const originalOrder = datasource @@ -334,9 +335,9 @@ export const buildExpression = ( arguments: { forAccessor: [yConfig.forAccessor], axisMode: yConfig.axisMode ? [yConfig.axisMode] : [], - color: yConfig.color ? [yConfig.color] : [], - lineStyle: yConfig.lineStyle ? [yConfig.lineStyle] : [], - lineWidth: yConfig.lineWidth ? [yConfig.lineWidth] : [], + color: [yConfig.color || defaultThresholdColor], + lineStyle: [yConfig.lineStyle || 'solid'], + lineWidth: [yConfig.lineWidth || 1], fill: [yConfig.fill || 'none'], icon: yConfig.icon ? [yConfig.icon] : [], }, diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index 8907db4954f99..8052b0d593215 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -347,9 +347,6 @@ describe('xy_visualization', () => { { axisMode: 'bottom', forAccessor: 'newCol', - icon: undefined, - lineStyle: 'solid', - lineWidth: 1, }, ], }); diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 33cd01c8fda7a..4e279d2e0026d 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -448,12 +448,7 @@ export const getXyVisualization = ({ if (!hasYConfig) { newLayer.yConfig = [ ...(newLayer.yConfig || []), - // TODO: move this - // add a default config if none is available { - icon: undefined, - lineStyle: 'solid', - lineWidth: 1, // override with previous styling, ...previousYConfig, // but keep the new group & id config diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx index 5a6458a4654d0..516adbf585b9f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx @@ -13,9 +13,13 @@ import { EuiFormRow, EuiColorPicker, EuiColorPickerProps, EuiToolTip, EuiIcon } import type { PaletteRegistry } from 'src/plugins/charts/public'; import type { VisualizationDimensionEditorProps } from '../../types'; import { State } from '../types'; -import { FormatFactory } from '../../../common'; +import { FormatFactory, layerTypes } from '../../../common'; import { getSeriesColor } from '../state_helpers'; -import { getAccessorColorConfig, getColorAssignments } from '../color_assignment'; +import { + defaultThresholdColor, + getAccessorColorConfig, + getColorAssignments, +} from '../color_assignment'; import { getSortedAccessors } from '../to_expression'; import { updateLayer } from '.'; import { TooltipWrapper } from '../../shared_components'; @@ -56,6 +60,9 @@ export const ColorPicker = ({ const overwriteColor = getSeriesColor(layer, accessor); const currentColor = useMemo(() => { if (overwriteColor || !frame.activeData) return overwriteColor; + if (layer.layerType === layerTypes.THRESHOLD) { + return defaultThresholdColor; + } const datasource = frame.datasourceLayers[layer.layerId]; const sortedAccessors: string[] = getSortedAccessors(datasource, layer); From 8cf80dad25fcb65f43e6e5ff994464cdc4354c90 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 06:47:18 -0400 Subject: [PATCH 33/51] [Stack Monitoring] Set beats react product name (fixes setup mode) (#113504) (#113599) Co-authored-by: Mat Schaffer --- .../monitoring/public/application/pages/beats/instances.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx b/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx index 873cc7e939269..7a65022d8ff53 100644 --- a/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx @@ -84,6 +84,7 @@ export const BeatsInstancesPage: React.FC = ({ clusters }) => { >
( {flyoutComponent} From 17a4b740d11b2fb2ea2961f84a976a58556e8079 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 1 Oct 2021 12:49:52 +0100 Subject: [PATCH 34/51] skip flaky suite (#112910) --- .../public/common/hooks/use_upgrade_secuirty_packages.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_upgrade_secuirty_packages.test.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_upgrade_secuirty_packages.test.tsx index f1d1b09f45f60..968bd8679b23b 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_upgrade_secuirty_packages.test.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_upgrade_secuirty_packages.test.tsx @@ -21,7 +21,8 @@ jest.mock('../components/user_privileges', () => { }); jest.mock('../lib/kibana'); -describe('When using the `useUpgradeSecurityPackages()` hook', () => { +// FLAKY: https://github.com/elastic/kibana/issues/112910 +describe.skip('When using the `useUpgradeSecurityPackages()` hook', () => { let renderResult: RenderHookResult; let renderHook: () => RenderHookResult; let kibana: ReturnType; From e30c1f532369db4c3931aad823ea756ec37d6919 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 10:24:03 -0400 Subject: [PATCH 35/51] chore: add modifications to staging automatically after eslint fix (#113443) (#113611) After precommit hook runs with --fix flag changes are not added to staging. However it also does not validate staging area since eslint is only looking for last changes on file not staging area this results fellows to commit with linting errors which fails in CI. This commit resolves this issue by adding fixed files right after linting to staging area. Closes #52722 Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Baturalp Gurdin <9674241+suchcodemuchwow@users.noreply.github.com> --- src/dev/run_precommit_hook.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/dev/run_precommit_hook.js b/src/dev/run_precommit_hook.js index e1eafaf28d95d..0a594a232cc22 100644 --- a/src/dev/run_precommit_hook.js +++ b/src/dev/run_precommit_hook.js @@ -6,6 +6,9 @@ * Side Public License, v 1. */ +import SimpleGit from 'simple-git/promise'; + +import { REPO_ROOT } from '@kbn/utils'; import { run, combineErrors, createFlagError, createFailError } from '@kbn/dev-utils'; import * as Eslint from './eslint'; import * as Stylelint from './stylelint'; @@ -48,6 +51,11 @@ run( await Linter.lintFiles(log, filesToLint, { fix: flags.fix, }); + + if (flags.fix) { + const simpleGit = new SimpleGit(REPO_ROOT); + await simpleGit.add(filesToLint); + } } catch (error) { errors.push(error); } From 4814df2e11652b7179bf71fcd4a287c7b2a9b32b Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 10:35:57 -0400 Subject: [PATCH 36/51] [Fleet] Show beats replacements in integration browser (#113291) (#113615) Display both beats and epr-packages in the integration browser. When there is overlap, the EPR-package equivalent is displayed. When the EPR-package is not yet ga, the beat-equivalent is displayed. Co-authored-by: Thomas Neirynck --- .../custom_integrations/common/index.ts | 14 +- .../custom_integrations/public/mocks.ts | 4 +- .../custom_integrations/public/plugin.ts | 14 +- .../custom_integrations/public/types.ts | 1 + .../custom_integration_registry.test.ts | 66 +++++- .../server/custom_integration_registry.ts | 22 +- .../custom_integrations/server/index.ts | 2 +- .../custom_integrations/server/plugin.test.ts | 1 - .../custom_integrations/server/plugin.ts | 3 - .../server/routes/define_routes.ts | 20 +- src/plugins/home/server/plugin.test.ts | 2 +- src/plugins/home/server/plugin.ts | 9 +- .../services/tutorials/lib/tutorial_schema.ts | 4 + .../tutorials/tutorials_registry.test.ts | 5 +- .../services/tutorials/tutorials_registry.ts | 47 +++- .../server/tutorials/activemq_logs/index.ts | 1 + .../tutorials/activemq_metrics/index.ts | 2 + .../home/server/tutorials/aws_logs/index.ts | 1 + .../server/tutorials/aws_metrics/index.ts | 1 + .../server/tutorials/cloudwatch_logs/index.ts | 1 + .../server/tutorials/oracle_metrics/index.ts | 1 + .../tutorials/prometheus_metrics/index.ts | 1 + .../apis/custom_integration/integrations.ts | 34 ++- .../epm/screens/home/category_facets.tsx | 12 +- .../sections/epm/screens/home/index.tsx | 45 ++-- .../sections/epm/screens/home/util.ts | 4 +- .../use_merge_epr_with_replacements.test.ts | 220 ++++++++++++++++++ .../hooks/use_merge_epr_with_replacements.ts | 71 ++++++ .../fleet/public/hooks/use_request/epm.ts | 7 +- 29 files changed, 542 insertions(+), 73 deletions(-) create mode 100644 x-pack/plugins/fleet/public/hooks/use_merge_epr_with_replacements.test.ts create mode 100644 x-pack/plugins/fleet/public/hooks/use_merge_epr_with_replacements.ts diff --git a/src/plugins/custom_integrations/common/index.ts b/src/plugins/custom_integrations/common/index.ts index 48f31cb0bcb0c..73e15c91ce4bf 100755 --- a/src/plugins/custom_integrations/common/index.ts +++ b/src/plugins/custom_integrations/common/index.ts @@ -9,12 +9,12 @@ export const PLUGIN_ID = 'customIntegrations'; export const PLUGIN_NAME = 'customIntegrations'; -export interface CategoryCount { +export interface IntegrationCategoryCount { count: number; - id: Category; + id: IntegrationCategory; } -export const CATEGORY_DISPLAY = { +export const INTEGRATION_CATEGORY_DISPLAY = { aws: 'AWS', azure: 'Azure', cloud: 'Cloud', @@ -44,7 +44,7 @@ export const CATEGORY_DISPLAY = { updates_available: 'Updates available', }; -export type Category = keyof typeof CATEGORY_DISPLAY; +export type IntegrationCategory = keyof typeof INTEGRATION_CATEGORY_DISPLAY; export interface CustomIntegrationIcon { src: string; @@ -59,8 +59,10 @@ export interface CustomIntegration { uiInternalPath: string; isBeta: boolean; icons: CustomIntegrationIcon[]; - categories: Category[]; + categories: IntegrationCategory[]; shipper: string; + eprOverlap?: string; // name of the equivalent Elastic Agent integration in EPR. e.g. a beat module can correspond to an EPR-package, or an APM-tutorial. When completed, Integrations-UX can preferentially show the EPR-package, rather than the custom-integration } -export const ROUTES_ADDABLECUSTOMINTEGRATIONS = `/api/${PLUGIN_ID}/appendCustomIntegrations`; +export const ROUTES_APPEND_CUSTOM_INTEGRATIONS = `/internal/${PLUGIN_ID}/appendCustomIntegrations`; +export const ROUTES_REPLACEMENT_CUSTOM_INTEGRATIONS = `/internal/${PLUGIN_ID}/replacementCustomIntegrations`; diff --git a/src/plugins/custom_integrations/public/mocks.ts b/src/plugins/custom_integrations/public/mocks.ts index e6462751368a3..2e6bc491c2c5c 100644 --- a/src/plugins/custom_integrations/public/mocks.ts +++ b/src/plugins/custom_integrations/public/mocks.ts @@ -9,10 +9,10 @@ import { CustomIntegrationsSetup } from './types'; function createCustomIntegrationsSetup(): jest.Mocked { - const mock = { + const mock: jest.Mocked = { getAppendCustomIntegrations: jest.fn(), + getReplacementCustomIntegrations: jest.fn(), }; - return mock; } diff --git a/src/plugins/custom_integrations/public/plugin.ts b/src/plugins/custom_integrations/public/plugin.ts index 821c08ce84e31..7ea7a829e8072 100755 --- a/src/plugins/custom_integrations/public/plugin.ts +++ b/src/plugins/custom_integrations/public/plugin.ts @@ -8,7 +8,11 @@ import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; import { CustomIntegrationsSetup, CustomIntegrationsStart } from './types'; -import { CustomIntegration, ROUTES_ADDABLECUSTOMINTEGRATIONS } from '../common'; +import { + CustomIntegration, + ROUTES_APPEND_CUSTOM_INTEGRATIONS, + ROUTES_REPLACEMENT_CUSTOM_INTEGRATIONS, +} from '../common'; export class CustomIntegrationsPlugin implements Plugin @@ -16,10 +20,14 @@ export class CustomIntegrationsPlugin public setup(core: CoreSetup): CustomIntegrationsSetup { // Return methods that should be available to other plugins return { + async getReplacementCustomIntegrations(): Promise { + return core.http.get(ROUTES_REPLACEMENT_CUSTOM_INTEGRATIONS); + }, + async getAppendCustomIntegrations(): Promise { - return core.http.get(ROUTES_ADDABLECUSTOMINTEGRATIONS); + return core.http.get(ROUTES_APPEND_CUSTOM_INTEGRATIONS); }, - } as CustomIntegrationsSetup; + }; } public start(core: CoreStart): CustomIntegrationsStart { diff --git a/src/plugins/custom_integrations/public/types.ts b/src/plugins/custom_integrations/public/types.ts index 911194171b4c4..9a12af767ecbc 100755 --- a/src/plugins/custom_integrations/public/types.ts +++ b/src/plugins/custom_integrations/public/types.ts @@ -10,6 +10,7 @@ import { CustomIntegration } from '../common'; export interface CustomIntegrationsSetup { getAppendCustomIntegrations: () => Promise; + getReplacementCustomIntegrations: () => Promise; } // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface CustomIntegrationsStart {} diff --git a/src/plugins/custom_integrations/server/custom_integration_registry.test.ts b/src/plugins/custom_integrations/server/custom_integration_registry.test.ts index 2e211cfb4c93d..8904aa8a257f6 100644 --- a/src/plugins/custom_integrations/server/custom_integration_registry.test.ts +++ b/src/plugins/custom_integrations/server/custom_integration_registry.test.ts @@ -8,7 +8,7 @@ import { CustomIntegrationRegistry } from './custom_integration_registry'; import { loggerMock, MockedLogger } from '@kbn/logging/mocks'; -import { CustomIntegration } from '../common'; +import { IntegrationCategory, CustomIntegration } from '../common'; describe('CustomIntegrationsRegistry', () => { let mockLogger: MockedLogger; @@ -44,6 +44,27 @@ describe('CustomIntegrationsRegistry', () => { expect(mockLogger.debug.mock.calls.length).toBe(1); }); }); + + test('should strip unsupported categories', () => { + const registry = new CustomIntegrationRegistry(mockLogger, true); + registry.registerCustomIntegration({ + ...integration, + categories: ['upload_file', 'foobar'] as IntegrationCategory[], + }); + expect(registry.getAppendCustomIntegrations()).toEqual([ + { + categories: ['upload_file'], + description: 'test integration', + icons: [], + id: 'foo', + isBeta: false, + shipper: 'tests', + title: 'Foo', + type: 'ui_link', + uiInternalPath: '/path/to/foo', + }, + ]); + }); }); describe('getAppendCustomCategories', () => { @@ -76,7 +97,7 @@ describe('CustomIntegrationsRegistry', () => { }, ]); }); - test('should ignore duplicate ids', () => { + test('should filter duplicate ids', () => { const registry = new CustomIntegrationRegistry(mockLogger, true); registry.registerCustomIntegration(integration); registry.registerCustomIntegration(integration); @@ -94,7 +115,7 @@ describe('CustomIntegrationsRegistry', () => { }, ]); }); - test('should ignore integrations without category', () => { + test('should filter integrations without category', () => { const registry = new CustomIntegrationRegistry(mockLogger, true); registry.registerCustomIntegration(integration); registry.registerCustomIntegration({ ...integration, id: 'bar', categories: [] }); @@ -113,5 +134,44 @@ describe('CustomIntegrationsRegistry', () => { }, ]); }); + + test('should filter integrations that need to replace EPR packages', () => { + const registry = new CustomIntegrationRegistry(mockLogger, true); + registry.registerCustomIntegration({ ...integration, id: 'bar', eprOverlap: 'aws' }); + expect(registry.getAppendCustomIntegrations()).toEqual([]); + }); + }); + + describe('getReplacementCustomIntegrations', () => { + test('should only return integrations with corresponding epr package ', () => { + const registry = new CustomIntegrationRegistry(mockLogger, true); + registry.registerCustomIntegration(integration); + registry.registerCustomIntegration({ ...integration, id: 'bar', eprOverlap: 'aws' }); + expect(registry.getReplacementCustomIntegrations()).toEqual([ + { + categories: ['upload_file'], + description: 'test integration', + icons: [], + id: 'bar', + isBeta: false, + shipper: 'tests', + title: 'Foo', + type: 'ui_link', + uiInternalPath: '/path/to/foo', + eprOverlap: 'aws', + }, + ]); + }); + + test('should filter registrations without valid categories', () => { + const registry = new CustomIntegrationRegistry(mockLogger, true); + registry.registerCustomIntegration({ + ...integration, + id: 'bar', + eprOverlap: 'aws', + categories: ['foobar'] as unknown as IntegrationCategory[], + }); + expect(registry.getReplacementCustomIntegrations()).toEqual([]); + }); }); }); diff --git a/src/plugins/custom_integrations/server/custom_integration_registry.ts b/src/plugins/custom_integrations/server/custom_integration_registry.ts index fa216ced5bd92..ba1b901b3fb29 100644 --- a/src/plugins/custom_integrations/server/custom_integration_registry.ts +++ b/src/plugins/custom_integrations/server/custom_integration_registry.ts @@ -7,10 +7,14 @@ */ import { Logger } from 'kibana/server'; -import { CustomIntegration } from '../common'; +import { IntegrationCategory, INTEGRATION_CATEGORY_DISPLAY, CustomIntegration } from '../common'; -function isAddable(integration: CustomIntegration) { - return integration.categories.length; +function isAddable(integration: CustomIntegration): boolean { + return !!integration.categories.length && !integration.eprOverlap; +} + +function isReplacement(integration: CustomIntegration): boolean { + return !!integration.categories.length && !!integration.eprOverlap; } export class CustomIntegrationRegistry { @@ -39,10 +43,20 @@ export class CustomIntegrationRegistry { return; } - this._integrations.push(customIntegration); + const allowedCategories: IntegrationCategory[] = (customIntegration.categories ?? []).filter( + (category) => { + return INTEGRATION_CATEGORY_DISPLAY.hasOwnProperty(category); + } + ) as IntegrationCategory[]; + + this._integrations.push({ ...customIntegration, categories: allowedCategories }); } getAppendCustomIntegrations(): CustomIntegration[] { return this._integrations.filter(isAddable); } + + getReplacementCustomIntegrations(): CustomIntegration[] { + return this._integrations.filter(isReplacement); + } } diff --git a/src/plugins/custom_integrations/server/index.ts b/src/plugins/custom_integrations/server/index.ts index 423a06009ac4b..490627ef90f8d 100755 --- a/src/plugins/custom_integrations/server/index.ts +++ b/src/plugins/custom_integrations/server/index.ts @@ -19,7 +19,7 @@ export function plugin(initializerContext: PluginInitializerContext) { export { CustomIntegrationsPluginSetup, CustomIntegrationsPluginStart } from './types'; -export type { Category, CategoryCount, CustomIntegration } from '../common'; +export type { IntegrationCategory, IntegrationCategoryCount, CustomIntegration } from '../common'; export const config = { schema: schema.object({}), diff --git a/src/plugins/custom_integrations/server/plugin.test.ts b/src/plugins/custom_integrations/server/plugin.test.ts index 08f68a70a3c70..424eedf0603cd 100644 --- a/src/plugins/custom_integrations/server/plugin.test.ts +++ b/src/plugins/custom_integrations/server/plugin.test.ts @@ -25,7 +25,6 @@ describe('CustomIntegrationsPlugin', () => { test('wires up tutorials provider service and returns registerTutorial and addScopedTutorialContextFactory', () => { const setup = new CustomIntegrationsPlugin(initContext).setup(mockCoreSetup); expect(setup).toHaveProperty('registerCustomIntegration'); - expect(setup).toHaveProperty('getAppendCustomIntegrations'); }); }); }); diff --git a/src/plugins/custom_integrations/server/plugin.ts b/src/plugins/custom_integrations/server/plugin.ts index f1ddd70b6945a..099650ee15a05 100755 --- a/src/plugins/custom_integrations/server/plugin.ts +++ b/src/plugins/custom_integrations/server/plugin.ts @@ -40,9 +40,6 @@ export class CustomIntegrationsPlugin ...integration, }); }, - getAppendCustomIntegrations: (): CustomIntegration[] => { - return this.customIngegrationRegistry.getAppendCustomIntegrations(); - }, } as CustomIntegrationsPluginSetup; } diff --git a/src/plugins/custom_integrations/server/routes/define_routes.ts b/src/plugins/custom_integrations/server/routes/define_routes.ts index f5e952a0c1ebd..77ec49363dc8f 100644 --- a/src/plugins/custom_integrations/server/routes/define_routes.ts +++ b/src/plugins/custom_integrations/server/routes/define_routes.ts @@ -8,7 +8,10 @@ import { IRouter } from 'src/core/server'; import { CustomIntegrationRegistry } from '../custom_integration_registry'; -import { ROUTES_ADDABLECUSTOMINTEGRATIONS } from '../../common'; +import { + ROUTES_APPEND_CUSTOM_INTEGRATIONS, + ROUTES_REPLACEMENT_CUSTOM_INTEGRATIONS, +} from '../../common'; export function defineRoutes( router: IRouter, @@ -16,7 +19,7 @@ export function defineRoutes( ) { router.get( { - path: ROUTES_ADDABLECUSTOMINTEGRATIONS, + path: ROUTES_APPEND_CUSTOM_INTEGRATIONS, validate: false, }, async (context, request, response) => { @@ -26,4 +29,17 @@ export function defineRoutes( }); } ); + + router.get( + { + path: ROUTES_REPLACEMENT_CUSTOM_INTEGRATIONS, + validate: false, + }, + async (context, request, response) => { + const integrations = customIntegrationsRegistry.getReplacementCustomIntegrations(); + return response.ok({ + body: integrations, + }); + } + ); } diff --git a/src/plugins/home/server/plugin.test.ts b/src/plugins/home/server/plugin.test.ts index 2dc8a8e6484aa..5af0c4a578eeb 100644 --- a/src/plugins/home/server/plugin.test.ts +++ b/src/plugins/home/server/plugin.test.ts @@ -77,7 +77,7 @@ describe('HomeServerPlugin', () => { test('is defined', () => { const plugin = new HomeServerPlugin(initContext); plugin.setup(mockCoreSetup, homeServerPluginSetupDependenciesMock); // setup() must always be called before start() - const start = plugin.start(); + const start = plugin.start(coreMock.createStart()); expect(start).toBeDefined(); expect(start).toHaveProperty('tutorials'); expect(start).toHaveProperty('sampleData'); diff --git a/src/plugins/home/server/plugin.ts b/src/plugins/home/server/plugin.ts index 5902544d9867f..98f7611655e55 100644 --- a/src/plugins/home/server/plugin.ts +++ b/src/plugins/home/server/plugin.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { CoreSetup, Plugin, PluginInitializerContext } from 'kibana/server'; +import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'kibana/server'; import { TutorialsRegistry, TutorialsRegistrySetup, @@ -30,8 +30,11 @@ export class HomeServerPlugin implements Plugin; diff --git a/src/plugins/home/server/services/tutorials/tutorials_registry.test.ts b/src/plugins/home/server/services/tutorials/tutorials_registry.test.ts index 15372949b1653..ee73c8e13f62b 100644 --- a/src/plugins/home/server/services/tutorials/tutorials_registry.test.ts +++ b/src/plugins/home/server/services/tutorials/tutorials_registry.test.ts @@ -149,7 +149,10 @@ describe('TutorialsRegistry', () => { describe('start', () => { test('exposes proper contract', () => { - const start = new TutorialsRegistry().start(); + const start = new TutorialsRegistry().start( + coreMock.createStart(), + mockCustomIntegrationsPluginSetup + ); expect(start).toBeDefined(); }); }); diff --git a/src/plugins/home/server/services/tutorials/tutorials_registry.ts b/src/plugins/home/server/services/tutorials/tutorials_registry.ts index 8f7ecd7d7ccf5..723c92e6dfaf4 100644 --- a/src/plugins/home/server/services/tutorials/tutorials_registry.ts +++ b/src/plugins/home/server/services/tutorials/tutorials_registry.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { CoreSetup } from 'src/core/server'; +import { CoreSetup, CoreStart } from 'src/core/server'; import { TutorialProvider, TutorialContextFactory, @@ -15,23 +15,17 @@ import { import { TutorialSchema, tutorialSchema } from './lib/tutorial_schema'; import { builtInTutorials } from '../../tutorials/register'; import { CustomIntegrationsPluginSetup } from '../../../../custom_integrations/server'; -import { Category, CATEGORY_DISPLAY } from '../../../../custom_integrations/common'; +import { IntegrationCategory } from '../../../../custom_integrations/common'; import { HOME_APP_BASE_PATH } from '../../../common/constants'; function registerTutorialWithCustomIntegrations( customIntegrations: CustomIntegrationsPluginSetup, tutorial: TutorialSchema ) { - const allowedCategories: Category[] = (tutorial.integrationBrowserCategories ?? []).filter( - (category) => { - return CATEGORY_DISPLAY.hasOwnProperty(category); - } - ) as Category[]; - customIntegrations.registerCustomIntegration({ id: tutorial.id, title: tutorial.name, - categories: allowedCategories, + categories: (tutorial.integrationBrowserCategories ?? []) as IntegrationCategory[], uiInternalPath: `${HOME_APP_BASE_PATH}#/tutorial/${tutorial.id}`, description: tutorial.shortDescription, icons: tutorial.euiIconType @@ -44,6 +38,32 @@ function registerTutorialWithCustomIntegrations( : [], shipper: 'tutorial', isBeta: false, + eprOverlap: tutorial.eprPackageOverlap, + }); +} + +function registerBeatsTutorialsWithCustomIntegrations( + core: CoreStart, + customIntegrations: CustomIntegrationsPluginSetup, + tutorial: TutorialSchema +) { + customIntegrations.registerCustomIntegration({ + id: tutorial.name, + title: tutorial.name, + categories: tutorial.integrationBrowserCategories as IntegrationCategory[], + uiInternalPath: `${HOME_APP_BASE_PATH}#/tutorial/${tutorial.id}`, + description: tutorial.shortDescription, + icons: tutorial.euiIconType + ? [ + { + type: tutorial.euiIconType.endsWith('svg') ? 'svg' : 'eui', + src: core.http.basePath.prepend(tutorial.euiIconType), + }, + ] + : [], + shipper: 'beats', + eprOverlap: tutorial.moduleName, + isBeta: false, }); } @@ -106,9 +126,16 @@ export class TutorialsRegistry { }; } - public start() { + public start(core: CoreStart, customIntegrations?: CustomIntegrationsPluginSetup) { // pre-populate with built in tutorials this.tutorialProviders.push(...builtInTutorials); + + if (customIntegrations) { + builtInTutorials.forEach((provider) => { + const tutorial = provider({}); + registerBeatsTutorialsWithCustomIntegrations(core, customIntegrations, tutorial); + }); + } return {}; } } diff --git a/src/plugins/home/server/tutorials/activemq_logs/index.ts b/src/plugins/home/server/tutorials/activemq_logs/index.ts index b1f9ee429473d..64a6fa575f5b6 100644 --- a/src/plugins/home/server/tutorials/activemq_logs/index.ts +++ b/src/plugins/home/server/tutorials/activemq_logs/index.ts @@ -58,5 +58,6 @@ export function activemqLogsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['web'], }; } diff --git a/src/plugins/home/server/tutorials/activemq_metrics/index.ts b/src/plugins/home/server/tutorials/activemq_metrics/index.ts index 49e27b7ce981c..7a59d6d4b70d1 100644 --- a/src/plugins/home/server/tutorials/activemq_metrics/index.ts +++ b/src/plugins/home/server/tutorials/activemq_metrics/index.ts @@ -56,5 +56,7 @@ export function activemqMetricsSpecProvider(context: TutorialContext): TutorialS onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + + integrationBrowserCategories: ['web'], }; } diff --git a/src/plugins/home/server/tutorials/aws_logs/index.ts b/src/plugins/home/server/tutorials/aws_logs/index.ts index 12eac20d5e4dc..3458800b33f0a 100644 --- a/src/plugins/home/server/tutorials/aws_logs/index.ts +++ b/src/plugins/home/server/tutorials/aws_logs/index.ts @@ -59,5 +59,6 @@ export function awsLogsSpecProvider(context: TutorialContext): TutorialSchema { onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['aws', 'cloud', 'datastore', 'security', 'network'], }; } diff --git a/src/plugins/home/server/tutorials/aws_metrics/index.ts b/src/plugins/home/server/tutorials/aws_metrics/index.ts index 2d4e10290affd..7c3a15a47d784 100644 --- a/src/plugins/home/server/tutorials/aws_metrics/index.ts +++ b/src/plugins/home/server/tutorials/aws_metrics/index.ts @@ -60,5 +60,6 @@ export function awsMetricsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['aws', 'cloud', 'datastore', 'security', 'network'], }; } diff --git a/src/plugins/home/server/tutorials/cloudwatch_logs/index.ts b/src/plugins/home/server/tutorials/cloudwatch_logs/index.ts index 41928a072aa6e..dd035a66c5ced 100644 --- a/src/plugins/home/server/tutorials/cloudwatch_logs/index.ts +++ b/src/plugins/home/server/tutorials/cloudwatch_logs/index.ts @@ -53,5 +53,6 @@ export function cloudwatchLogsSpecProvider(context: TutorialContext): TutorialSc onPrem: onPremInstructions([], context), elasticCloud: cloudInstructions(), onPremElasticCloud: onPremCloudInstructions(), + integrationBrowserCategories: ['security', 'network', 'web'], }; } diff --git a/src/plugins/home/server/tutorials/oracle_metrics/index.ts b/src/plugins/home/server/tutorials/oracle_metrics/index.ts index 37fc345820298..42560a7b46225 100644 --- a/src/plugins/home/server/tutorials/oracle_metrics/index.ts +++ b/src/plugins/home/server/tutorials/oracle_metrics/index.ts @@ -57,5 +57,6 @@ export function oracleMetricsSpecProvider(context: TutorialContext): TutorialSch onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/prometheus_metrics/index.ts b/src/plugins/home/server/tutorials/prometheus_metrics/index.ts index b0179fbc82614..ee05770d65108 100644 --- a/src/plugins/home/server/tutorials/prometheus_metrics/index.ts +++ b/src/plugins/home/server/tutorials/prometheus_metrics/index.ts @@ -57,5 +57,6 @@ export function prometheusMetricsSpecProvider(context: TutorialContext): Tutoria onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['monitoring', 'datastore'], }; } diff --git a/test/api_integration/apis/custom_integration/integrations.ts b/test/api_integration/apis/custom_integration/integrations.ts index 2d1d085198bb4..4b1344ee8e84c 100644 --- a/test/api_integration/apis/custom_integration/integrations.ts +++ b/test/api_integration/apis/custom_integration/integrations.ts @@ -12,18 +12,32 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - describe('get list of append integrations', () => { - it('should return list of custom integrations that can be appended', async () => { - const resp = await supertest - .get(`/api/customIntegrations/appendCustomIntegrations`) - .set('kbn-xsrf', 'kibana') - .expect(200); + describe('customIntegrations', () => { + describe('get list of append integrations', () => { + it('should return list of custom integrations that can be appended', async () => { + const resp = await supertest + .get(`/internal/customIntegrations/appendCustomIntegrations`) + .set('kbn-xsrf', 'kibana') + .expect(200); - expect(resp.body).to.be.an('array'); - expect(resp.body.length).to.be.above(2); // Should at least have registered the three sample data-sets + expect(resp.body).to.be.an('array'); + expect(resp.body.length).to.be.above(2); // Should at least have registered the three sample data-sets - ['flights', 'logs', 'ecommerce'].forEach((sampleData) => { - expect(resp.body.findIndex((c: { id: string }) => c.id === sampleData)).to.be.above(-1); + ['flights', 'logs', 'ecommerce'].forEach((sampleData) => { + expect(resp.body.findIndex((c: { id: string }) => c.id === sampleData)).to.be.above(-1); + }); + }); + }); + + describe('get list of replacement integrations', () => { + it('should return list of custom integrations that can be used to replace EPR packages', async () => { + const resp = await supertest + .get(`/internal/customIntegrations/replacementCustomIntegrations`) + .set('kbn-xsrf', 'kibana') + .expect(200); + + expect(resp.body).to.be.an('array'); + expect(resp.body.length).to.be.above(2); // Should have at least a few beats registered }); }); }); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/category_facets.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/category_facets.tsx index daa0520ebc041..d60ccd93b8db1 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/category_facets.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/category_facets.tsx @@ -11,22 +11,24 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { Loading } from '../../../../components'; -import type { CategoryCount } from '../../../../../../../../../../src/plugins/custom_integrations/common'; -import { CATEGORY_DISPLAY } from '../../../../../../../../../../src/plugins/custom_integrations/common'; +import type { IntegrationCategoryCount } from '../../../../../../../../../../src/plugins/custom_integrations/common'; +import { INTEGRATION_CATEGORY_DISPLAY } from '../../../../../../../../../../src/plugins/custom_integrations/common'; interface ALL_CATEGORY { id: ''; count: number; } -export type CategoryFacet = CategoryCount | ALL_CATEGORY; +export type CategoryFacet = IntegrationCategoryCount | ALL_CATEGORY; export function CategoryFacets({ + showCounts, isLoading, categories, selectedCategory, onCategoryChange, }: { + showCounts: boolean; isLoading?: boolean; categories: CategoryFacet[]; selectedCategory: string; @@ -49,14 +51,14 @@ export function CategoryFacets({ defaultMessage: 'All', }); } else { - title = CATEGORY_DISPLAY[category.id]; + title = INTEGRATION_CATEGORY_DISPLAY[category.id]; } return ( onCategoryChange(category)} > {title} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx index 48a9dc0f6b63c..27e2d11769ef8 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx @@ -21,7 +21,8 @@ import { useGetCategories, useGetPackages, useBreadcrumbs, - useGetAddableCustomIntegrations, + useGetAppendCustomIntegrations, + useGetReplacementCustomIntegrations, useLink, } from '../../../../hooks'; import { doesPackageHaveIntegrations } from '../../../../services'; @@ -35,7 +36,9 @@ import type { PackageListItem } from '../../../../types'; import type { IntegrationCardItem } from '../../../../../../../common/types/models'; -import type { Category } from '../../../../../../../../../../src/plugins/custom_integrations/common'; +import type { IntegrationCategory } from '../../../../../../../../../../src/plugins/custom_integrations/common'; + +import { useMergeEprPackagesWithReplacements } from '../../../../../../hooks/use_merge_epr_with_replacements'; import { mergeAndReplaceCategoryCounts } from './util'; import { CategoryFacets } from './category_facets'; @@ -209,6 +212,7 @@ const InstalledPackages: React.FC = memo(() => { const controls = ( setSelectedCategory(id)} @@ -266,6 +270,7 @@ const AvailablePackages: React.FC = memo(() => { const { data: categoriesRes, isLoading: isLoadingCategories } = useGetCategories({ include_policy_templates: true, }); + const eprPackages = useMemo( () => packageListToIntegrationsList(categoryPackagesRes?.response || []), [categoryPackagesRes] @@ -276,14 +281,23 @@ const AvailablePackages: React.FC = memo(() => { [allCategoryPackagesRes] ); - const { loading: isLoadingAddableCustomIntegrations, value: addableCustomIntegrations } = - useGetAddableCustomIntegrations(); - const filteredAddableIntegrations = addableCustomIntegrations - ? addableCustomIntegrations.filter((integration: CustomIntegration) => { + const { value: replacementCustomIntegrations } = useGetReplacementCustomIntegrations(); + + const mergedEprPackages: Array = + useMergeEprPackagesWithReplacements( + eprPackages || [], + replacementCustomIntegrations || [], + selectedCategory as IntegrationCategory + ); + + const { loading: isLoadingAppendCustomIntegrations, value: appendCustomIntegrations } = + useGetAppendCustomIntegrations(); + const filteredAddableIntegrations = appendCustomIntegrations + ? appendCustomIntegrations.filter((integration: CustomIntegration) => { if (!selectedCategory) { return true; } - return integration.categories.indexOf(selectedCategory as Category) >= 0; + return integration.categories.indexOf(selectedCategory as IntegrationCategory) >= 0; }) : []; @@ -296,7 +310,7 @@ const AvailablePackages: React.FC = memo(() => { ); const eprAndCustomPackages: Array = [ - ...eprPackages, + ...mergedEprPackages, ...filteredAddableIntegrations, ]; eprAndCustomPackages.sort((a, b) => { @@ -306,26 +320,26 @@ const AvailablePackages: React.FC = memo(() => { const categories = useMemo(() => { const eprAndCustomCategories: CategoryFacet[] = isLoadingCategories || - isLoadingAddableCustomIntegrations || - !addableCustomIntegrations || + isLoadingAppendCustomIntegrations || + !appendCustomIntegrations || !categoriesRes ? [] : mergeAndReplaceCategoryCounts( categoriesRes.response as CategoryFacet[], - addableCustomIntegrations + appendCustomIntegrations ); return [ { id: '', - count: (allEprPackages?.length || 0) + (addableCustomIntegrations?.length || 0), + count: (allEprPackages?.length || 0) + (appendCustomIntegrations?.length || 0), }, ...(eprAndCustomCategories ? eprAndCustomCategories : []), ] as CategoryFacet[]; }, [ allEprPackages?.length, - addableCustomIntegrations, + appendCustomIntegrations, categoriesRes, - isLoadingAddableCustomIntegrations, + isLoadingAppendCustomIntegrations, isLoadingCategories, ]); @@ -336,7 +350,8 @@ const AvailablePackages: React.FC = memo(() => { const controls = categories ? ( { diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/util.ts b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/util.ts index 2f0e4f71ea59d..53a62555650ab 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/util.ts +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/util.ts @@ -7,7 +7,7 @@ import type { CustomIntegration, - Category, + IntegrationCategory, } from '../../../../../../../../../../src/plugins/custom_integrations/common'; import type { CategoryFacet } from './category_facets'; @@ -27,7 +27,7 @@ export function mergeAndReplaceCategoryCounts( match.count += count; } else { merged.push({ - id: category as Category, + id: category as IntegrationCategory, count, }); } diff --git a/x-pack/plugins/fleet/public/hooks/use_merge_epr_with_replacements.test.ts b/x-pack/plugins/fleet/public/hooks/use_merge_epr_with_replacements.test.ts new file mode 100644 index 0000000000000..687fb01b04546 --- /dev/null +++ b/x-pack/plugins/fleet/public/hooks/use_merge_epr_with_replacements.test.ts @@ -0,0 +1,220 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { PackageListItem } from '../../common/types/models'; +import type { CustomIntegration } from '../../../../../src/plugins/custom_integrations/common'; + +import type { IntegrationCategory } from '../../../../../src/plugins/custom_integrations/common'; + +import { useMergeEprPackagesWithReplacements } from './use_merge_epr_with_replacements'; + +function mockEprPackages( + items: Array<{ name: string; release: 'ga' | 'beta' | 'experimental'; integration?: string }> +): PackageListItem[] { + return items as unknown as PackageListItem[]; +} + +function mockIntegrations( + items: Array<{ eprOverlap?: string; id: string; categories: IntegrationCategory[] }> +): CustomIntegration[] { + return items as unknown as CustomIntegration[]; +} + +describe('useMergeEprWithReplacements', () => { + test('should not replace ga packages', () => { + const eprPackages: PackageListItem[] = mockEprPackages([ + { + name: 'aws', + release: 'ga', + integration: 'cloudwatch', + }, + { + name: 'aws', + release: 'ga', + integration: 's3', + }, + ]); + const replacements: CustomIntegration[] = mockIntegrations([ + { + eprOverlap: 'aws', + id: 'awsLogs', + categories: ['cloud', 'datastore'], + }, + ]); + + expect(useMergeEprPackagesWithReplacements(eprPackages, replacements, '')).toEqual([ + { + name: 'aws', + release: 'ga', + integration: 'cloudwatch', + }, + { + name: 'aws', + release: 'ga', + integration: 's3', + }, + ]); + }); + + test('should replace non-ga packages', () => { + const eprPackages: PackageListItem[] = mockEprPackages([ + { + name: 'activemq', + release: 'beta', + }, + ]); + const replacements: CustomIntegration[] = mockIntegrations([ + { + eprOverlap: 'activemq', + id: 'activemq-logs', + categories: ['web'], + }, + { + eprOverlap: 'activemq', + id: 'activemq-metrics', + categories: ['web'], + }, + ]); + + expect(useMergeEprPackagesWithReplacements(eprPackages, replacements, '')).toEqual([ + { + eprOverlap: 'activemq', + id: 'activemq-logs', + categories: ['web'], + }, + { + eprOverlap: 'activemq', + id: 'activemq-metrics', + categories: ['web'], + }, + ]); + }); + + test('should merge if no equivalent package', () => { + const eprPackages: PackageListItem[] = mockEprPackages([ + { + name: 'activemq', + release: 'beta', + }, + ]); + const replacements: CustomIntegration[] = mockIntegrations([ + { + id: 'prometheus', + categories: ['monitoring', 'datastore'], + }, + ]); + + expect(useMergeEprPackagesWithReplacements(eprPackages, replacements, '')).toEqual([ + { + name: 'activemq', + release: 'beta', + }, + { + id: 'prometheus', + categories: ['monitoring', 'datastore'], + }, + ]); + }); + + test('should respect category assignment', () => { + const eprPackages: PackageListItem[] = mockEprPackages([ + { + name: 'activemq', + release: 'beta', + }, + ]); + const replacements: CustomIntegration[] = mockIntegrations([ + { + id: 'prometheus', + categories: ['monitoring', 'datastore'], + }, + { + id: 'oracle', + categories: ['datastore'], + }, + ]); + + expect(useMergeEprPackagesWithReplacements(eprPackages, replacements, 'web')).toEqual([ + { + name: 'activemq', + release: 'beta', + }, + ]); + }); + + test('should consists of all 3 types (ga eprs, replacements for non-ga eprs, replacements without epr equivalent', () => { + const eprPackages: PackageListItem[] = mockEprPackages([ + { + name: 'aws', + release: 'ga', + integration: 'cloudwatch', + }, + { + name: 'aws', + release: 'ga', + integration: 's3', + }, + { + name: 'activemq', + release: 'beta', + }, + ]); + const replacements: CustomIntegration[] = mockIntegrations([ + { + id: 'prometheus', + categories: ['monitoring', 'datastore'], + }, + { + eprOverlap: 'activemq', + id: 'activemq-logs', + categories: ['web'], + }, + { + eprOverlap: 'activemq', + id: 'activemq-metrics', + categories: ['web'], + }, + { + eprOverlap: 'aws', + id: 'awsLogs', + categories: ['cloud', 'datastore'], + }, + { + eprOverlap: 'aws', + id: 'awsMetrics', + categories: ['cloud', 'datastore'], + }, + ]); + + expect(useMergeEprPackagesWithReplacements(eprPackages, replacements, '')).toEqual([ + { + name: 'aws', + release: 'ga', + integration: 'cloudwatch', + }, + { + name: 'aws', + release: 'ga', + integration: 's3', + }, + { + eprOverlap: 'activemq', + id: 'activemq-logs', + categories: ['web'], + }, + { + eprOverlap: 'activemq', + id: 'activemq-metrics', + categories: ['web'], + }, + { + id: 'prometheus', + categories: ['monitoring', 'datastore'], + }, + ]); + }); +}); diff --git a/x-pack/plugins/fleet/public/hooks/use_merge_epr_with_replacements.ts b/x-pack/plugins/fleet/public/hooks/use_merge_epr_with_replacements.ts new file mode 100644 index 0000000000000..a3c1fea5e744f --- /dev/null +++ b/x-pack/plugins/fleet/public/hooks/use_merge_epr_with_replacements.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { PackageListItem } from '../../common/types/models'; +import type { + CustomIntegration, + IntegrationCategory, +} from '../../../../../src/plugins/custom_integrations/common'; + +// Export this as a utility to find replacements for a package (e.g. in the overview-page for an EPR package) +function findReplacementsForEprPackage( + replacements: CustomIntegration[], + packageName: string, + release: 'beta' | 'experimental' | 'ga' +): CustomIntegration[] { + if (release === 'ga') { + return []; + } + return replacements.filter((customIntegration: CustomIntegration) => { + return customIntegration.eprOverlap === packageName; + }); +} + +export function useMergeEprPackagesWithReplacements( + eprPackages: PackageListItem[], + replacements: CustomIntegration[], + category: IntegrationCategory | '' +): Array { + const merged: Array = []; + + const filteredReplacements = replacements.filter((customIntegration) => { + return !category || customIntegration.categories.includes(category); + }); + + // Either select replacement or select beat + eprPackages.forEach((eprPackage) => { + const hits = findReplacementsForEprPackage( + filteredReplacements, + eprPackage.name, + eprPackage.release + ); + if (hits.length) { + hits.forEach((hit) => { + const match = merged.find(({ id }) => { + return id === hit.id; + }); + if (!match) { + merged.push(hit); + } + }); + } else { + merged.push(eprPackage); + } + }); + + // Add unused replacements + // This is an edge-case. E.g. the Oracle-beat did not have an Epr-equivalent at the time of writing + const unusedReplacements = filteredReplacements.filter((integration) => { + return !eprPackages.find((eprPackage) => { + return eprPackage.name === integration.eprOverlap; + }); + }); + + merged.push(...unusedReplacements); + + return merged; +} diff --git a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts index 650667000409a..d6764aac7de00 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts @@ -24,11 +24,16 @@ import { getCustomIntegrations } from '../../services/custom_integrations'; import { useRequest, sendRequest } from './use_request'; -export function useGetAddableCustomIntegrations() { +export function useGetAppendCustomIntegrations() { const customIntegrations = getCustomIntegrations(); return useAsync(customIntegrations.getAppendCustomIntegrations, []); } +export function useGetReplacementCustomIntegrations() { + const customIntegrations = getCustomIntegrations(); + return useAsync(customIntegrations.getReplacementCustomIntegrations, []); +} + export const useGetCategories = (query: GetCategoriesRequest['query'] = {}) => { return useRequest({ path: epmRouteService.getCategoriesPath(), From 196ed668f6d2500fb6f507fb6c4a0ba4a6450329 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 10:37:12 -0400 Subject: [PATCH 37/51] fix sorting, pagination, state (#113563) (#113613) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Sandra G --- .../public/application/hooks/use_table.ts | 57 +++++-------------- .../components/elasticsearch/nodes/nodes.js | 5 +- .../pipeline_listing/pipeline_listing.js | 4 +- .../public/components/table/eui_table_ssp.js | 26 +++++---- 4 files changed, 34 insertions(+), 58 deletions(-) diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_table.ts b/x-pack/plugins/monitoring/public/application/hooks/use_table.ts index 8c27a826564ee..2e6018ec89809 100644 --- a/x-pack/plugins/monitoring/public/application/hooks/use_table.ts +++ b/x-pack/plugins/monitoring/public/application/hooks/use_table.ts @@ -6,7 +6,6 @@ */ import { useState, useCallback } from 'react'; -import { EUI_SORT_ASCENDING } from '../../../common/constants'; import { euiTableStorageGetter, euiTableStorageSetter } from '../../components/table'; import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; @@ -83,22 +82,18 @@ export function useTable(storageKey: string) { // get initial state from localStorage const [sorting, setSorting] = useState(storageData.sort || { sort: {} }); - const cleanSortingData = (sortData: Sorting) => { - const sort = sortData || { sort: {} }; - - if (!sort.sort.field) { - sort.sort.field = 'name'; - } - if (!sort.sort.direction) { - sort.sort.direction = EUI_SORT_ASCENDING; - } - - return sort; - }; const [query, setQuery] = useState(''); - const onTableChange = ({ page, sort }: { page: Page; sort: Sorting['sort'] }) => { + const onTableChange = ({ + page, + sort, + queryText, + }: { + page: Page; + sort: Sorting['sort']; + queryText: string; + }) => { setPagination({ ...pagination, ...{ @@ -109,11 +104,14 @@ export function useTable(storageKey: string) { pageSizeOptions: PAGE_SIZE_OPTIONS, }, }); - setSorting(cleanSortingData({ sort })); + setSorting({ sort }); setLocalStorageData(storage, { page, - sort: { sort }, + sort: { + sort, + }, }); + setQuery(queryText); }; const getPaginationRouteOptions = useCallback(() => { @@ -136,33 +134,6 @@ export function useTable(storageKey: string) { sorting, pagination, onTableChange, - fetchMoreData: ({ - page, - sort, - queryText, - }: { - page: Page; - sort: Sorting; - queryText: string; - }) => { - setPagination({ - ...pagination, - ...{ - initialPageSize: page.size, - pageSize: page.size, - initialPageIndex: page.index, - pageIndex: page.index, - pageSizeOptions: PAGE_SIZE_OPTIONS, - }, - }); - setSorting(cleanSortingData(sort)); - setQuery(queryText); - - setLocalStorageData(storage, { - page, - sort, - }); - }, }; }; diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js index 73cf31dbe5376..bae0ca3a42cfd 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js @@ -296,8 +296,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler }; export function ElasticsearchNodes({ clusterStatus, showCgroupMetricsElasticsearch, ...props }) { - const { sorting, pagination, onTableChange, clusterUuid, setupMode, fetchMoreData, alerts } = - props; + const { sorting, pagination, onTableChange, clusterUuid, setupMode, alerts } = props; const columns = getColumns(showCgroupMetricsElasticsearch, setupMode, clusterUuid, alerts); @@ -464,7 +463,7 @@ export function ElasticsearchNodes({ clusterStatus, showCgroupMetricsElasticsear }, }} onTableChange={onTableChange} - fetchMoreData={fetchMoreData} + {...props} /> diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_listing/pipeline_listing.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_listing/pipeline_listing.js index 85d55683a6574..6c3d059f6be56 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_listing/pipeline_listing.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_listing/pipeline_listing.js @@ -127,7 +127,7 @@ export class PipelineListing extends Component { } render() { - const { data, sorting, pagination, onTableChange, fetchMoreData, upgradeMessage, className } = + const { data, sorting, pagination, onTableChange, upgradeMessage, className, ...props } = this.props; const sortingOptions = sorting || { field: 'id', direction: 'asc' }; @@ -149,7 +149,6 @@ export class PipelineListing extends Component { sorting={sortingOptions} message={upgradeMessage} pagination={pagination} - fetchMoreData={fetchMoreData} search={{ box: { placeholder: i18n.translate( @@ -161,6 +160,7 @@ export class PipelineListing extends Component { }, }} onTableChange={onTableChange} + {...props} /> diff --git a/x-pack/plugins/monitoring/public/components/table/eui_table_ssp.js b/x-pack/plugins/monitoring/public/components/table/eui_table_ssp.js index 5042c3c938fb7..cbf589d5de2a0 100644 --- a/x-pack/plugins/monitoring/public/components/table/eui_table_ssp.js +++ b/x-pack/plugins/monitoring/public/components/table/eui_table_ssp.js @@ -20,10 +20,8 @@ export function EuiMonitoringSSPTable({ onTableChange, setupMode, productName, - fetchMoreData, ...props }) { - const [isLoading, setIsLoading] = React.useState(false); const [queryText, setQueryText] = React.useState(''); const [page, setPage] = React.useState({ index: pagination.pageIndex, @@ -72,19 +70,28 @@ export function EuiMonitoringSSPTable({ const onChange = async ({ page, sort }) => { setPage(page); setSort({ sort }); - setIsLoading(true); - await fetchMoreData({ page, sort: { sort }, queryText }); - setIsLoading(false); - onTableChange({ page, sort }); + // angular version + if (props.fetchMoreData) { + await props.fetchMoreData({ page, sort: { sort }, queryText }); + onTableChange({ page, sort }); + } + // react version + else { + onTableChange({ page, sort, queryText }); + } }; const onQueryChange = async ({ queryText }) => { const newPage = { ...page, index: 0 }; setPage(newPage); setQueryText(queryText); - setIsLoading(true); - await fetchMoreData({ page: newPage, sort, queryText }); - setIsLoading(false); + // angular version + if (props.fetchMoreData) { + await props.fetchMoreData({ page: newPage, sort, queryText }); + } else { + // react version + onTableChange({ page, sort: sort.sort, queryText }); + } }; return ( @@ -97,7 +104,6 @@ export function EuiMonitoringSSPTable({ items={items} pagination={pagination} onChange={onChange} - loading={isLoading} columns={columns} /> {footerContent} From d8a310ed6ea97f10d3fc02ce61d22cd741559db8 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 10:48:09 -0400 Subject: [PATCH 38/51] [Discover] Save collapse sidebar state in local storage (#113287) (#113616) * [Discover] fix selected fields persist * [Discover] apply solution for entire sidebar instead of "Selected fields" collapse button * [Discover] update unit tests * [Discover] update unit tests * [Discover] improve tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Dmitry Tomashevich <39378793+Dmitriynj@users.noreply.github.com> --- .../discover/public/__mocks__/services.ts | 3 ++ .../layout/discover_layout.test.tsx | 34 +++++++++++++++++-- .../components/layout/discover_layout.tsx | 18 ++++++++-- .../sidebar/discover_sidebar_responsive.tsx | 2 +- src/plugins/discover/public/build_services.ts | 6 +++- 5 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/plugins/discover/public/__mocks__/services.ts b/src/plugins/discover/public/__mocks__/services.ts index d24aefb6c8192..30d66b113e528 100644 --- a/src/plugins/discover/public/__mocks__/services.ts +++ b/src/plugins/discover/public/__mocks__/services.ts @@ -89,4 +89,7 @@ export const discoverServiceMock = { useChartsTheme: jest.fn(() => EUI_CHARTS_THEME_LIGHT.theme), useChartsBaseTheme: jest.fn(() => EUI_CHARTS_THEME_LIGHT.theme), }, + storage: { + get: jest.fn(), + }, } as unknown as DiscoverServices; diff --git a/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.test.tsx b/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.test.tsx index 7b2825907ba29..6ebed3185e2f1 100644 --- a/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.test.tsx +++ b/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.test.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { Subject, BehaviorSubject } from 'rxjs'; import { mountWithIntl } from '@kbn/test/jest'; import { setHeaderActionMenuMounter } from '../../../../../kibana_services'; -import { DiscoverLayout } from './discover_layout'; +import { DiscoverLayout, SIDEBAR_CLOSED_KEY } from './discover_layout'; import { esHits } from '../../../../../__mocks__/es_hits'; import { indexPatternMock } from '../../../../../__mocks__/index_pattern'; import { savedSearchMock } from '../../../../../__mocks__/saved_search'; @@ -31,15 +31,21 @@ import { FetchStatus } from '../../../../types'; import { ElasticSearchHit } from '../../../../doc_views/doc_views_types'; import { RequestAdapter } from '../../../../../../../inspector'; import { Chart } from '../chart/point_series'; +import { DiscoverSidebar } from '../sidebar/discover_sidebar'; setHeaderActionMenuMounter(jest.fn()); -function getProps(indexPattern: IndexPattern): DiscoverLayoutProps { +function getProps(indexPattern: IndexPattern, wasSidebarClosed?: boolean): DiscoverLayoutProps { const searchSourceMock = createSearchSourceMock({}); const services = discoverServiceMock; services.data.query.timefilter.timefilter.getAbsoluteTime = () => { return { from: '2020-05-14T11:05:13.590', to: '2020-05-14T11:20:13.590' }; }; + services.storage.get = (key: string) => { + if (key === SIDEBAR_CLOSED_KEY) { + return wasSidebarClosed; + } + }; const indexPatternList = [indexPattern].map((ip) => { return { ...ip, ...{ attributes: { title: ip.title } } }; @@ -139,10 +145,34 @@ describe('Discover component', () => { const component = mountWithIntl(); expect(component.find('[data-test-subj="discoverChartOptionsToggle"]').exists()).toBeFalsy(); }); + test('selected index pattern with time field displays chart toggle', () => { const component = mountWithIntl( ); expect(component.find('[data-test-subj="discoverChartOptionsToggle"]').exists()).toBeTruthy(); }); + + describe('sidebar', () => { + test('should be opened if discover:sidebarClosed was not set', () => { + const component = mountWithIntl( + + ); + expect(component.find(DiscoverSidebar).length).toBe(1); + }); + + test('should be opened if discover:sidebarClosed is false', () => { + const component = mountWithIntl( + + ); + expect(component.find(DiscoverSidebar).length).toBe(1); + }); + + test('should be closed if discover:sidebarClosed is true', () => { + const component = mountWithIntl( + + ); + expect(component.find(DiscoverSidebar).length).toBe(0); + }); + }); }); diff --git a/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.tsx index c2d09f31e3e0a..4bbef32dcbadd 100644 --- a/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.tsx @@ -40,6 +40,11 @@ import { DiscoverDocuments } from './discover_documents'; import { FetchStatus } from '../../../../types'; import { useDataState } from '../../utils/use_data_state'; +/** + * Local storage key for sidebar persistence state + */ +export const SIDEBAR_CLOSED_KEY = 'discover:sidebarClosed'; + const SidebarMemoized = React.memo(DiscoverSidebarResponsive); const TopNavMemoized = React.memo(DiscoverTopNav); const DiscoverChartMemoized = React.memo(DiscoverChart); @@ -60,7 +65,8 @@ export function DiscoverLayout({ state, stateContainer, }: DiscoverLayoutProps) { - const { trackUiMetric, capabilities, indexPatterns, data, uiSettings, filterManager } = services; + const { trackUiMetric, capabilities, indexPatterns, data, uiSettings, filterManager, storage } = + services; const { main$, charts$, totalHits$ } = savedSearchData$; const [expandedDoc, setExpandedDoc] = useState(undefined); @@ -78,7 +84,8 @@ export function DiscoverLayout({ return indexPattern.type !== 'rollup' ? indexPattern.timeFieldName : undefined; }, [indexPattern]); - const [isSidebarClosed, setIsSidebarClosed] = useState(false); + const initialSidebarClosed = Boolean(storage.get(SIDEBAR_CLOSED_KEY)); + const [isSidebarClosed, setIsSidebarClosed] = useState(initialSidebarClosed); const useNewFieldsApi = useMemo(() => !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE), [uiSettings]); const resultState = useMemo( @@ -144,6 +151,11 @@ export function DiscoverLayout({ filterManager.setFilters(disabledFilters); }, [filterManager]); + const toggleSidebarCollapse = useCallback(() => { + storage.set(SIDEBAR_CLOSED_KEY, !isSidebarClosed); + setIsSidebarClosed(!isSidebarClosed); + }, [isSidebarClosed, storage]); + const contentCentered = resultState === 'uninitialized' || resultState === 'none'; return ( @@ -192,7 +204,7 @@ export function DiscoverLayout({ iconType={isSidebarClosed ? 'menuRight' : 'menuLeft'} iconSize="m" size="xs" - onClick={() => setIsSidebarClosed(!isSidebarClosed)} + onClick={toggleSidebarCollapse} data-test-subj="collapseSideBarButton" aria-controls="discover-sidebar" aria-expanded={isSidebarClosed ? 'false' : 'true'} diff --git a/src/plugins/discover/public/application/apps/main/components/sidebar/discover_sidebar_responsive.tsx b/src/plugins/discover/public/application/apps/main/components/sidebar/discover_sidebar_responsive.tsx index ade9c3aae4b28..90357b73c6881 100644 --- a/src/plugins/discover/public/application/apps/main/components/sidebar/discover_sidebar_responsive.tsx +++ b/src/plugins/discover/public/application/apps/main/components/sidebar/discover_sidebar_responsive.tsx @@ -217,7 +217,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) return ( <> - {props.isClosed ? null : ( + {!props.isClosed && ( void; indexPatternFieldEditor: IndexPatternFieldEditorStart; http: HttpStart; + storage: Storage; } export function buildServices( @@ -75,6 +77,7 @@ export function buildServices( }; const savedObjectService = createSavedSearchesLoader(services); const { usageCollection } = plugins; + const storage = new Storage(localStorage); return { addBasePath: core.http.basePath.prepend, @@ -100,6 +103,7 @@ export function buildServices( timefilter: plugins.data.query.timefilter.timefilter, toastNotifications: core.notifications.toasts, uiSettings: core.uiSettings, + storage, trackUiMetric: usageCollection?.reportUiCounter.bind(usageCollection, 'discover'), indexPatternFieldEditor: plugins.indexPatternFieldEditor, http: core.http, From da2f48caef4d78f9779709586aa2bc07e4d57d55 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Fri, 1 Oct 2021 09:08:27 -0700 Subject: [PATCH 39/51] Revert "chore: add modifications to staging automatically after eslint fix (#113443) (#113611)" This reverts commit e30c1f532369db4c3931aad823ea756ec37d6919. --- src/dev/run_precommit_hook.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/dev/run_precommit_hook.js b/src/dev/run_precommit_hook.js index 0a594a232cc22..e1eafaf28d95d 100644 --- a/src/dev/run_precommit_hook.js +++ b/src/dev/run_precommit_hook.js @@ -6,9 +6,6 @@ * Side Public License, v 1. */ -import SimpleGit from 'simple-git/promise'; - -import { REPO_ROOT } from '@kbn/utils'; import { run, combineErrors, createFlagError, createFailError } from '@kbn/dev-utils'; import * as Eslint from './eslint'; import * as Stylelint from './stylelint'; @@ -51,11 +48,6 @@ run( await Linter.lintFiles(log, filesToLint, { fix: flags.fix, }); - - if (flags.fix) { - const simpleGit = new SimpleGit(REPO_ROOT); - await simpleGit.add(filesToLint); - } } catch (error) { errors.push(error); } From c39e72ac385d46c4d70fb2be5cf47d7a27080089 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 13:51:40 -0400 Subject: [PATCH 40/51] Exclude cypress folders from Jest test coverage (#113482) (#113639) Co-authored-by: Scotty Bollinger --- x-pack/plugins/enterprise_search/jest.config.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/plugins/enterprise_search/jest.config.js b/x-pack/plugins/enterprise_search/jest.config.js index 263713697b7e0..b5e6105ff41f2 100644 --- a/x-pack/plugins/enterprise_search/jest.config.js +++ b/x-pack/plugins/enterprise_search/jest.config.js @@ -18,4 +18,8 @@ module.exports = { '!/x-pack/plugins/enterprise_search/public/applications/test_helpers/**/*.{ts,tsx}', ], coverageDirectory: '/target/kibana-coverage/jest/x-pack/plugins/enterprise_search', + modulePathIgnorePatterns: [ + '/x-pack/plugins/enterprise_search/public/applications/app_search/cypress', + '/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress', + ], }; From 8956e7f55e13d4bf56c966fdd21fb80dab0c4480 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 14:16:06 -0400 Subject: [PATCH 41/51] [buildkite] Add a minimal flaky test suite runner job (#113575) (#113657) Co-authored-by: Brian Seeders --- .buildkite/pipelines/flaky_tests/pipeline.js | 52 +++++++++++++ .buildkite/pipelines/flaky_tests/pipeline.sh | 5 ++ .buildkite/pipelines/flaky_tests/runner.js | 82 ++++++++++++++++++++ .buildkite/pipelines/flaky_tests/runner.sh | 8 ++ 4 files changed, 147 insertions(+) create mode 100644 .buildkite/pipelines/flaky_tests/pipeline.js create mode 100755 .buildkite/pipelines/flaky_tests/pipeline.sh create mode 100644 .buildkite/pipelines/flaky_tests/runner.js create mode 100755 .buildkite/pipelines/flaky_tests/runner.sh diff --git a/.buildkite/pipelines/flaky_tests/pipeline.js b/.buildkite/pipelines/flaky_tests/pipeline.js new file mode 100644 index 0000000000000..1d390ed0d1b63 --- /dev/null +++ b/.buildkite/pipelines/flaky_tests/pipeline.js @@ -0,0 +1,52 @@ +const stepInput = (key, nameOfSuite) => { + return { + key: `ftsr-suite/${key}`, + text: nameOfSuite, + required: false, + default: '0', + }; +}; + +const OSS_CI_GROUPS = 12; +const XPACK_CI_GROUPS = 13; + +const inputs = [ + { + key: 'ftsr-override-count', + text: 'Override for all suites', + default: 0, + required: true, + }, + { + key: 'ftsr-concurrency', + text: 'Max concurrency per step', + default: 20, + required: true, + }, +]; + +for (let i = 1; i <= OSS_CI_GROUPS; i++) { + inputs.push(stepInput(`oss/cigroup/${i}`, `OSS CI Group ${i}`)); +} + +for (let i = 1; i <= XPACK_CI_GROUPS; i++) { + inputs.push(stepInput(`xpack/cigroup/${i}`, `Default CI Group ${i}`)); +} + +const pipeline = { + steps: [ + { + input: 'Number of Runs', + fields: inputs, + }, + { + wait: '~', + }, + { + command: '.buildkite/pipelines/flaky_tests/runner.sh', + label: 'Create pipeline', + }, + ], +}; + +console.log(JSON.stringify(pipeline, null, 2)); diff --git a/.buildkite/pipelines/flaky_tests/pipeline.sh b/.buildkite/pipelines/flaky_tests/pipeline.sh new file mode 100755 index 0000000000000..6335cd5490af0 --- /dev/null +++ b/.buildkite/pipelines/flaky_tests/pipeline.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -euo pipefail + +node .buildkite/pipelines/flaky_tests/pipeline.js | buildkite-agent pipeline upload diff --git a/.buildkite/pipelines/flaky_tests/runner.js b/.buildkite/pipelines/flaky_tests/runner.js new file mode 100644 index 0000000000000..46c390ce455ca --- /dev/null +++ b/.buildkite/pipelines/flaky_tests/runner.js @@ -0,0 +1,82 @@ +const { execSync } = require('child_process'); + +const keys = execSync('buildkite-agent meta-data keys') + .toString() + .split('\n') + .filter((k) => k.startsWith('ftsr-suite/')); + +const overrideCount = parseInt( + execSync(`buildkite-agent meta-data get 'ftsr-override-count'`).toString().trim() +); + +const concurrency = + parseInt(execSync(`buildkite-agent meta-data get 'ftsr-concurrency'`).toString().trim()) || 20; + +const testSuites = []; +for (const key of keys) { + if (!key) { + continue; + } + + const value = + overrideCount || execSync(`buildkite-agent meta-data get '${key}'`).toString().trim(); + + testSuites.push({ + key: key.replace('ftsr-suite/', ''), + count: value === '' ? defaultCount : parseInt(value), + }); +} + +const steps = []; +const pipeline = { + env: { + IGNORE_SHIP_CI_STATS_ERROR: 'true', + }, + steps: steps, +}; + +steps.push({ + command: '.buildkite/scripts/steps/build_kibana.sh', + label: 'Build Kibana Distribution and Plugins', + agents: { queue: 'c2-8' }, + key: 'build', + if: "build.env('BUILD_ID_FOR_ARTIFACTS') == null || build.env('BUILD_ID_FOR_ARTIFACTS') == ''", +}); + +for (const testSuite of testSuites) { + const TEST_SUITE = testSuite.key; + const RUN_COUNT = testSuite.count; + const UUID = TEST_SUITE + process.env.UUID; + + const JOB_PARTS = TEST_SUITE.split('/'); + const IS_XPACK = JOB_PARTS[0] === 'xpack'; + const CI_GROUP = JOB_PARTS.length > 2 ? JOB_PARTS[2] : ''; + + if (RUN_COUNT < 1) { + continue; + } + + if (IS_XPACK) { + steps.push({ + command: `CI_GROUP=${CI_GROUP} .buildkite/scripts/steps/functional/xpack_cigroup.sh`, + label: `Default CI Group ${CI_GROUP}`, + agents: { queue: 'ci-group-6' }, + depends_on: 'build', + parallelism: RUN_COUNT, + concurrency: concurrency, + concurrency_group: UUID, + }); + } else { + steps.push({ + command: `CI_GROUP=${CI_GROUP} .buildkite/scripts/steps/functional/oss_cigroup.sh`, + label: `OSS CI Group ${CI_GROUP}`, + agents: { queue: 'ci-group-4d' }, + depends_on: 'build', + parallelism: RUN_COUNT, + concurrency: concurrency, + concurrency_group: UUID, + }); + } +} + +console.log(JSON.stringify(pipeline, null, 2)); diff --git a/.buildkite/pipelines/flaky_tests/runner.sh b/.buildkite/pipelines/flaky_tests/runner.sh new file mode 100755 index 0000000000000..b541af88a408a --- /dev/null +++ b/.buildkite/pipelines/flaky_tests/runner.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -euo pipefail + +UUID="$(cat /proc/sys/kernel/random/uuid)" +export UUID + +node .buildkite/pipelines/flaky_tests/runner.js | buildkite-agent pipeline upload From 6b068c59728a490a2d828e130eda2b28a8d752f5 Mon Sep 17 00:00:00 2001 From: Dmitry Tomashevich <39378793+Dmitriynj@users.noreply.github.com> Date: Fri, 1 Oct 2021 22:21:47 +0300 Subject: [PATCH 42/51] [Discover] Fix doc viewer table columns (#113124) (#113637) * [Discover] fix doc viewer table columns * [Discover] apply suggestions * [Discover] apply suggestion * [Discover] fix action column * [Discover] do not apply min-width to actions column Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/application/components/doc_viewer/doc_viewer.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.scss b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.scss index 83b5ce2849c90..16712ae8dbb78 100644 --- a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.scss +++ b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.scss @@ -12,6 +12,11 @@ font-size: $euiFontSizeXS; font-family: $euiCodeFontFamily; + // set min-width for each column except actions + .euiTableRowCell:nth-child(n+2) { + min-width: $euiSizeM * 9; + } + .kbnDocViewer__buttons { // Show all icons if one is focused, &:focus-within { From f730038fff9cf88a745f601120342c0cf3a2eb8c Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 15:29:49 -0400 Subject: [PATCH 43/51] Annotates beats tutorials with custom integration categories (#113565) (#113652) Co-authored-by: Christiane (Tina) Heiligers --- src/plugins/home/server/tutorials/aerospike_metrics/index.ts | 1 + src/plugins/home/server/tutorials/apache_logs/index.ts | 1 + src/plugins/home/server/tutorials/apache_metrics/index.ts | 1 + src/plugins/home/server/tutorials/auditbeat/index.ts | 1 + src/plugins/home/server/tutorials/auditd_logs/index.ts | 1 + src/plugins/home/server/tutorials/azure_logs/index.ts | 1 + src/plugins/home/server/tutorials/azure_metrics/index.ts | 1 + src/plugins/home/server/tutorials/barracuda_logs/index.ts | 1 + src/plugins/home/server/tutorials/bluecoat_logs/index.ts | 1 + src/plugins/home/server/tutorials/cef_logs/index.ts | 1 + src/plugins/home/server/tutorials/ceph_metrics/index.ts | 1 + src/plugins/home/server/tutorials/checkpoint_logs/index.ts | 1 + src/plugins/home/server/tutorials/cisco_logs/index.ts | 1 + src/plugins/home/server/tutorials/cockroachdb_metrics/index.ts | 1 + src/plugins/home/server/tutorials/consul_metrics/index.ts | 1 + src/plugins/home/server/tutorials/coredns_logs/index.ts | 1 + src/plugins/home/server/tutorials/coredns_metrics/index.ts | 1 + src/plugins/home/server/tutorials/couchbase_metrics/index.ts | 1 + src/plugins/home/server/tutorials/couchdb_metrics/index.ts | 1 + src/plugins/home/server/tutorials/crowdstrike_logs/index.ts | 1 + src/plugins/home/server/tutorials/cylance_logs/index.ts | 1 + src/plugins/home/server/tutorials/docker_metrics/index.ts | 1 + src/plugins/home/server/tutorials/dropwizard_metrics/index.ts | 1 + src/plugins/home/server/tutorials/elasticsearch_logs/index.ts | 1 + src/plugins/home/server/tutorials/elasticsearch_metrics/index.ts | 1 + src/plugins/home/server/tutorials/envoyproxy_logs/index.ts | 1 + src/plugins/home/server/tutorials/envoyproxy_metrics/index.ts | 1 + src/plugins/home/server/tutorials/etcd_metrics/index.ts | 1 + src/plugins/home/server/tutorials/f5_logs/index.ts | 1 + src/plugins/home/server/tutorials/fortinet_logs/index.ts | 1 + src/plugins/home/server/tutorials/gcp_logs/index.ts | 1 + src/plugins/home/server/tutorials/gcp_metrics/index.ts | 1 + src/plugins/home/server/tutorials/golang_metrics/index.ts | 1 + src/plugins/home/server/tutorials/gsuite_logs/index.ts | 1 + src/plugins/home/server/tutorials/haproxy_logs/index.ts | 1 + src/plugins/home/server/tutorials/haproxy_metrics/index.ts | 1 + src/plugins/home/server/tutorials/ibmmq_logs/index.ts | 1 + src/plugins/home/server/tutorials/ibmmq_metrics/index.ts | 1 + src/plugins/home/server/tutorials/icinga_logs/index.ts | 1 + src/plugins/home/server/tutorials/iis_logs/index.ts | 1 + src/plugins/home/server/tutorials/iis_metrics/index.ts | 1 + src/plugins/home/server/tutorials/imperva_logs/index.ts | 1 + src/plugins/home/server/tutorials/infoblox_logs/index.ts | 1 + src/plugins/home/server/tutorials/iptables_logs/index.ts | 1 + src/plugins/home/server/tutorials/juniper_logs/index.ts | 1 + src/plugins/home/server/tutorials/kafka_logs/index.ts | 1 + src/plugins/home/server/tutorials/kafka_metrics/index.ts | 1 + src/plugins/home/server/tutorials/kibana_logs/index.ts | 1 + src/plugins/home/server/tutorials/kibana_metrics/index.ts | 1 + src/plugins/home/server/tutorials/kubernetes_metrics/index.ts | 1 + src/plugins/home/server/tutorials/logstash_logs/index.ts | 1 + src/plugins/home/server/tutorials/logstash_metrics/index.ts | 1 + src/plugins/home/server/tutorials/memcached_metrics/index.ts | 1 + src/plugins/home/server/tutorials/microsoft_logs/index.ts | 1 + src/plugins/home/server/tutorials/misp_logs/index.ts | 1 + src/plugins/home/server/tutorials/mongodb_logs/index.ts | 1 + src/plugins/home/server/tutorials/mongodb_metrics/index.ts | 1 + src/plugins/home/server/tutorials/mssql_logs/index.ts | 1 + src/plugins/home/server/tutorials/mssql_metrics/index.ts | 1 + src/plugins/home/server/tutorials/munin_metrics/index.ts | 1 + src/plugins/home/server/tutorials/mysql_logs/index.ts | 1 + src/plugins/home/server/tutorials/mysql_metrics/index.ts | 1 + src/plugins/home/server/tutorials/nats_logs/index.ts | 1 + src/plugins/home/server/tutorials/nats_metrics/index.ts | 1 + src/plugins/home/server/tutorials/netflow_logs/index.ts | 1 + src/plugins/home/server/tutorials/netscout_logs/index.ts | 1 + src/plugins/home/server/tutorials/nginx_logs/index.ts | 1 + src/plugins/home/server/tutorials/nginx_metrics/index.ts | 1 + src/plugins/home/server/tutorials/o365_logs/index.ts | 1 + src/plugins/home/server/tutorials/okta_logs/index.ts | 1 + src/plugins/home/server/tutorials/openmetrics_metrics/index.ts | 1 + src/plugins/home/server/tutorials/osquery_logs/index.ts | 1 + src/plugins/home/server/tutorials/panw_logs/index.ts | 1 + src/plugins/home/server/tutorials/php_fpm_metrics/index.ts | 1 + src/plugins/home/server/tutorials/postgresql_logs/index.ts | 1 + src/plugins/home/server/tutorials/postgresql_metrics/index.ts | 1 + src/plugins/home/server/tutorials/rabbitmq_logs/index.ts | 1 + src/plugins/home/server/tutorials/rabbitmq_metrics/index.ts | 1 + src/plugins/home/server/tutorials/radware_logs/index.ts | 1 + src/plugins/home/server/tutorials/redis_logs/index.ts | 1 + src/plugins/home/server/tutorials/redis_metrics/index.ts | 1 + .../home/server/tutorials/redisenterprise_metrics/index.ts | 1 + src/plugins/home/server/tutorials/santa_logs/index.ts | 1 + src/plugins/home/server/tutorials/sonicwall_logs/index.ts | 1 + src/plugins/home/server/tutorials/sophos_logs/index.ts | 1 + src/plugins/home/server/tutorials/squid_logs/index.ts | 1 + src/plugins/home/server/tutorials/stan_metrics/index.ts | 1 + src/plugins/home/server/tutorials/statsd_metrics/index.ts | 1 + src/plugins/home/server/tutorials/suricata_logs/index.ts | 1 + src/plugins/home/server/tutorials/system_logs/index.ts | 1 + src/plugins/home/server/tutorials/system_metrics/index.ts | 1 + src/plugins/home/server/tutorials/tomcat_logs/index.ts | 1 + src/plugins/home/server/tutorials/traefik_logs/index.ts | 1 + src/plugins/home/server/tutorials/traefik_metrics/index.ts | 1 + src/plugins/home/server/tutorials/uptime_monitors/index.ts | 1 + src/plugins/home/server/tutorials/uwsgi_metrics/index.ts | 1 + src/plugins/home/server/tutorials/vsphere_metrics/index.ts | 1 + src/plugins/home/server/tutorials/windows_event_logs/index.ts | 1 + src/plugins/home/server/tutorials/windows_metrics/index.ts | 1 + src/plugins/home/server/tutorials/zeek_logs/index.ts | 1 + src/plugins/home/server/tutorials/zookeeper_metrics/index.ts | 1 + src/plugins/home/server/tutorials/zscaler_logs/index.ts | 1 + 102 files changed, 102 insertions(+) diff --git a/src/plugins/home/server/tutorials/aerospike_metrics/index.ts b/src/plugins/home/server/tutorials/aerospike_metrics/index.ts index 27246b78b1d6b..75dd45272db69 100644 --- a/src/plugins/home/server/tutorials/aerospike_metrics/index.ts +++ b/src/plugins/home/server/tutorials/aerospike_metrics/index.ts @@ -56,5 +56,6 @@ export function aerospikeMetricsSpecProvider(context: TutorialContext): Tutorial onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['web'], }; } diff --git a/src/plugins/home/server/tutorials/apache_logs/index.ts b/src/plugins/home/server/tutorials/apache_logs/index.ts index 06d2035ee4713..8606a40fe0a23 100644 --- a/src/plugins/home/server/tutorials/apache_logs/index.ts +++ b/src/plugins/home/server/tutorials/apache_logs/index.ts @@ -59,5 +59,6 @@ export function apacheLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['web'], }; } diff --git a/src/plugins/home/server/tutorials/apache_metrics/index.ts b/src/plugins/home/server/tutorials/apache_metrics/index.ts index e94f64ce1f058..f013f3da737f0 100644 --- a/src/plugins/home/server/tutorials/apache_metrics/index.ts +++ b/src/plugins/home/server/tutorials/apache_metrics/index.ts @@ -58,5 +58,6 @@ export function apacheMetricsSpecProvider(context: TutorialContext): TutorialSch onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['web'], }; } diff --git a/src/plugins/home/server/tutorials/auditbeat/index.ts b/src/plugins/home/server/tutorials/auditbeat/index.ts index dd51f0e512a5b..8bd6450b1daa4 100644 --- a/src/plugins/home/server/tutorials/auditbeat/index.ts +++ b/src/plugins/home/server/tutorials/auditbeat/index.ts @@ -58,5 +58,6 @@ processes, users, logins, sockets information, file accesses, and more. \ onPrem: onPremInstructions(platforms, context), elasticCloud: cloudInstructions(platforms), onPremElasticCloud: onPremCloudInstructions(platforms), + integrationBrowserCategories: ['web'], }; } diff --git a/src/plugins/home/server/tutorials/auditd_logs/index.ts b/src/plugins/home/server/tutorials/auditd_logs/index.ts index 5e1cea86ccfa7..a0d6f5f683e2c 100644 --- a/src/plugins/home/server/tutorials/auditd_logs/index.ts +++ b/src/plugins/home/server/tutorials/auditd_logs/index.ts @@ -59,5 +59,6 @@ export function auditdLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['os_system'], }; } diff --git a/src/plugins/home/server/tutorials/azure_logs/index.ts b/src/plugins/home/server/tutorials/azure_logs/index.ts index 8d8b1f2813c6a..2bf1527a79c40 100644 --- a/src/plugins/home/server/tutorials/azure_logs/index.ts +++ b/src/plugins/home/server/tutorials/azure_logs/index.ts @@ -60,5 +60,6 @@ export function azureLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['azure', 'cloud', 'network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/azure_metrics/index.ts b/src/plugins/home/server/tutorials/azure_metrics/index.ts index 9dff3956b745f..4a6112510b333 100644 --- a/src/plugins/home/server/tutorials/azure_metrics/index.ts +++ b/src/plugins/home/server/tutorials/azure_metrics/index.ts @@ -59,5 +59,6 @@ export function azureMetricsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['azure', 'cloud', 'network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/barracuda_logs/index.ts b/src/plugins/home/server/tutorials/barracuda_logs/index.ts index c85e92d884f93..35ce10e00892e 100644 --- a/src/plugins/home/server/tutorials/barracuda_logs/index.ts +++ b/src/plugins/home/server/tutorials/barracuda_logs/index.ts @@ -56,5 +56,6 @@ export function barracudaLogsSpecProvider(context: TutorialContext): TutorialSch onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/bluecoat_logs/index.ts b/src/plugins/home/server/tutorials/bluecoat_logs/index.ts index b9a0eb6459b99..85c7dff85d3e6 100644 --- a/src/plugins/home/server/tutorials/bluecoat_logs/index.ts +++ b/src/plugins/home/server/tutorials/bluecoat_logs/index.ts @@ -56,5 +56,6 @@ export function bluecoatLogsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/cef_logs/index.ts b/src/plugins/home/server/tutorials/cef_logs/index.ts index 4204614fadc58..cfd267f661d2a 100644 --- a/src/plugins/home/server/tutorials/cef_logs/index.ts +++ b/src/plugins/home/server/tutorials/cef_logs/index.ts @@ -63,5 +63,6 @@ export function cefLogsSpecProvider(context: TutorialContext): TutorialSchema { onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/ceph_metrics/index.ts b/src/plugins/home/server/tutorials/ceph_metrics/index.ts index 41e47655656ac..821067d87c905 100644 --- a/src/plugins/home/server/tutorials/ceph_metrics/index.ts +++ b/src/plugins/home/server/tutorials/ceph_metrics/index.ts @@ -56,5 +56,6 @@ export function cephMetricsSpecProvider(context: TutorialContext): TutorialSchem onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/checkpoint_logs/index.ts b/src/plugins/home/server/tutorials/checkpoint_logs/index.ts index 7171b09b02492..9c0d5591ae35b 100644 --- a/src/plugins/home/server/tutorials/checkpoint_logs/index.ts +++ b/src/plugins/home/server/tutorials/checkpoint_logs/index.ts @@ -56,5 +56,6 @@ export function checkpointLogsSpecProvider(context: TutorialContext): TutorialSc onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/cisco_logs/index.ts b/src/plugins/home/server/tutorials/cisco_logs/index.ts index f2b21013037bb..50b79f448b316 100644 --- a/src/plugins/home/server/tutorials/cisco_logs/index.ts +++ b/src/plugins/home/server/tutorials/cisco_logs/index.ts @@ -59,5 +59,6 @@ export function ciscoLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/cockroachdb_metrics/index.ts b/src/plugins/home/server/tutorials/cockroachdb_metrics/index.ts index a6ed3ad22ff7f..e43d05a0a098f 100644 --- a/src/plugins/home/server/tutorials/cockroachdb_metrics/index.ts +++ b/src/plugins/home/server/tutorials/cockroachdb_metrics/index.ts @@ -61,5 +61,6 @@ export function cockroachdbMetricsSpecProvider(context: TutorialContext): Tutori onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['security', 'network', 'web'], }; } diff --git a/src/plugins/home/server/tutorials/consul_metrics/index.ts b/src/plugins/home/server/tutorials/consul_metrics/index.ts index fd82227f01fcb..915920db5882c 100644 --- a/src/plugins/home/server/tutorials/consul_metrics/index.ts +++ b/src/plugins/home/server/tutorials/consul_metrics/index.ts @@ -58,5 +58,6 @@ export function consulMetricsSpecProvider(context: TutorialContext): TutorialSch onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['security', 'network', 'web'], }; } diff --git a/src/plugins/home/server/tutorials/coredns_logs/index.ts b/src/plugins/home/server/tutorials/coredns_logs/index.ts index 48b9154b81f4b..298464651f7fc 100644 --- a/src/plugins/home/server/tutorials/coredns_logs/index.ts +++ b/src/plugins/home/server/tutorials/coredns_logs/index.ts @@ -59,5 +59,6 @@ export function corednsLogsSpecProvider(context: TutorialContext): TutorialSchem onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security', 'network', 'web'], }; } diff --git a/src/plugins/home/server/tutorials/coredns_metrics/index.ts b/src/plugins/home/server/tutorials/coredns_metrics/index.ts index 50c0e5d46f86a..34912efb31a81 100644 --- a/src/plugins/home/server/tutorials/coredns_metrics/index.ts +++ b/src/plugins/home/server/tutorials/coredns_metrics/index.ts @@ -56,5 +56,6 @@ export function corednsMetricsSpecProvider(context: TutorialContext): TutorialSc onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['security', 'network', 'web'], }; } diff --git a/src/plugins/home/server/tutorials/couchbase_metrics/index.ts b/src/plugins/home/server/tutorials/couchbase_metrics/index.ts index de8c306480774..1860991fd17b2 100644 --- a/src/plugins/home/server/tutorials/couchbase_metrics/index.ts +++ b/src/plugins/home/server/tutorials/couchbase_metrics/index.ts @@ -56,5 +56,6 @@ export function couchbaseMetricsSpecProvider(context: TutorialContext): Tutorial onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['security', 'network', 'web'], }; } diff --git a/src/plugins/home/server/tutorials/couchdb_metrics/index.ts b/src/plugins/home/server/tutorials/couchdb_metrics/index.ts index d4f4f2c5bc04c..a6c57f56cf2e1 100644 --- a/src/plugins/home/server/tutorials/couchdb_metrics/index.ts +++ b/src/plugins/home/server/tutorials/couchdb_metrics/index.ts @@ -61,5 +61,6 @@ export function couchdbMetricsSpecProvider(context: TutorialContext): TutorialSc onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['security', 'network', 'web'], }; } diff --git a/src/plugins/home/server/tutorials/crowdstrike_logs/index.ts b/src/plugins/home/server/tutorials/crowdstrike_logs/index.ts index a61ad14f9793a..baaaef50a641f 100644 --- a/src/plugins/home/server/tutorials/crowdstrike_logs/index.ts +++ b/src/plugins/home/server/tutorials/crowdstrike_logs/index.ts @@ -59,5 +59,6 @@ export function crowdstrikeLogsSpecProvider(context: TutorialContext): TutorialS onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/cylance_logs/index.ts b/src/plugins/home/server/tutorials/cylance_logs/index.ts index 489e2c49d765e..9766f417b8870 100644 --- a/src/plugins/home/server/tutorials/cylance_logs/index.ts +++ b/src/plugins/home/server/tutorials/cylance_logs/index.ts @@ -56,5 +56,6 @@ export function cylanceLogsSpecProvider(context: TutorialContext): TutorialSchem onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/docker_metrics/index.ts b/src/plugins/home/server/tutorials/docker_metrics/index.ts index 9b4dd919b338b..6a8687ef5d66e 100644 --- a/src/plugins/home/server/tutorials/docker_metrics/index.ts +++ b/src/plugins/home/server/tutorials/docker_metrics/index.ts @@ -58,5 +58,6 @@ export function dockerMetricsSpecProvider(context: TutorialContext): TutorialSch onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['containers', 'os_system'], }; } diff --git a/src/plugins/home/server/tutorials/dropwizard_metrics/index.ts b/src/plugins/home/server/tutorials/dropwizard_metrics/index.ts index 6283147bc3bac..86be26dd12ca7 100644 --- a/src/plugins/home/server/tutorials/dropwizard_metrics/index.ts +++ b/src/plugins/home/server/tutorials/dropwizard_metrics/index.ts @@ -56,5 +56,6 @@ export function dropwizardMetricsSpecProvider(context: TutorialContext): Tutoria onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['elastic_stack', 'datastore'], }; } diff --git a/src/plugins/home/server/tutorials/elasticsearch_logs/index.ts b/src/plugins/home/server/tutorials/elasticsearch_logs/index.ts index 94e904eb33247..1886a912fdcd2 100644 --- a/src/plugins/home/server/tutorials/elasticsearch_logs/index.ts +++ b/src/plugins/home/server/tutorials/elasticsearch_logs/index.ts @@ -58,5 +58,6 @@ export function elasticsearchLogsSpecProvider(context: TutorialContext): Tutoria onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['containers', 'os_system'], }; } diff --git a/src/plugins/home/server/tutorials/elasticsearch_metrics/index.ts b/src/plugins/home/server/tutorials/elasticsearch_metrics/index.ts index 48fa3491e43b6..2adc2fd90fa70 100644 --- a/src/plugins/home/server/tutorials/elasticsearch_metrics/index.ts +++ b/src/plugins/home/server/tutorials/elasticsearch_metrics/index.ts @@ -56,5 +56,6 @@ export function elasticsearchMetricsSpecProvider(context: TutorialContext): Tuto onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['elastic_stack', 'datastore'], }; } diff --git a/src/plugins/home/server/tutorials/envoyproxy_logs/index.ts b/src/plugins/home/server/tutorials/envoyproxy_logs/index.ts index 74add28c1ae74..fda69a2467b25 100644 --- a/src/plugins/home/server/tutorials/envoyproxy_logs/index.ts +++ b/src/plugins/home/server/tutorials/envoyproxy_logs/index.ts @@ -62,5 +62,6 @@ export function envoyproxyLogsSpecProvider(context: TutorialContext): TutorialSc onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['elastic_stack', 'datastore'], }; } diff --git a/src/plugins/home/server/tutorials/envoyproxy_metrics/index.ts b/src/plugins/home/server/tutorials/envoyproxy_metrics/index.ts index 18d3dfae392e7..263d1a2036fd0 100644 --- a/src/plugins/home/server/tutorials/envoyproxy_metrics/index.ts +++ b/src/plugins/home/server/tutorials/envoyproxy_metrics/index.ts @@ -49,5 +49,6 @@ export function envoyproxyMetricsSpecProvider(context: TutorialContext): Tutoria onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['elastic_stack', 'datastore'], }; } diff --git a/src/plugins/home/server/tutorials/etcd_metrics/index.ts b/src/plugins/home/server/tutorials/etcd_metrics/index.ts index 288124c1efbe9..cda16ecf68e34 100644 --- a/src/plugins/home/server/tutorials/etcd_metrics/index.ts +++ b/src/plugins/home/server/tutorials/etcd_metrics/index.ts @@ -56,5 +56,6 @@ export function etcdMetricsSpecProvider(context: TutorialContext): TutorialSchem onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['elastic_stack', 'datastore'], }; } diff --git a/src/plugins/home/server/tutorials/f5_logs/index.ts b/src/plugins/home/server/tutorials/f5_logs/index.ts index 976c207305376..ebcdd4ece7f45 100644 --- a/src/plugins/home/server/tutorials/f5_logs/index.ts +++ b/src/plugins/home/server/tutorials/f5_logs/index.ts @@ -57,5 +57,6 @@ export function f5LogsSpecProvider(context: TutorialContext): TutorialSchema { onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/fortinet_logs/index.ts b/src/plugins/home/server/tutorials/fortinet_logs/index.ts index 250e873d037b5..3e7923b680c6e 100644 --- a/src/plugins/home/server/tutorials/fortinet_logs/index.ts +++ b/src/plugins/home/server/tutorials/fortinet_logs/index.ts @@ -56,5 +56,6 @@ export function fortinetLogsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/gcp_logs/index.ts b/src/plugins/home/server/tutorials/gcp_logs/index.ts index 6aa6adb183418..feef7d673c5d9 100644 --- a/src/plugins/home/server/tutorials/gcp_logs/index.ts +++ b/src/plugins/home/server/tutorials/gcp_logs/index.ts @@ -61,5 +61,6 @@ export function gcpLogsSpecProvider(context: TutorialContext): TutorialSchema { onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['google_cloud', 'cloud', 'network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/gcp_metrics/index.ts b/src/plugins/home/server/tutorials/gcp_metrics/index.ts index 8416ab105de96..5f198ed5f3cf2 100644 --- a/src/plugins/home/server/tutorials/gcp_metrics/index.ts +++ b/src/plugins/home/server/tutorials/gcp_metrics/index.ts @@ -60,5 +60,6 @@ export function gcpMetricsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['google_cloud', 'cloud', 'network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/golang_metrics/index.ts b/src/plugins/home/server/tutorials/golang_metrics/index.ts index 1d298fd294a3e..85937e0dda0e0 100644 --- a/src/plugins/home/server/tutorials/golang_metrics/index.ts +++ b/src/plugins/home/server/tutorials/golang_metrics/index.ts @@ -59,5 +59,6 @@ export function golangMetricsSpecProvider(context: TutorialContext): TutorialSch onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['google_cloud', 'cloud', 'network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/gsuite_logs/index.ts b/src/plugins/home/server/tutorials/gsuite_logs/index.ts index 2973af4b969c7..4d23c6b1cfdce 100644 --- a/src/plugins/home/server/tutorials/gsuite_logs/index.ts +++ b/src/plugins/home/server/tutorials/gsuite_logs/index.ts @@ -56,5 +56,6 @@ export function gsuiteLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/haproxy_logs/index.ts b/src/plugins/home/server/tutorials/haproxy_logs/index.ts index 9aa853af7819e..0b0fd35f07058 100644 --- a/src/plugins/home/server/tutorials/haproxy_logs/index.ts +++ b/src/plugins/home/server/tutorials/haproxy_logs/index.ts @@ -59,5 +59,6 @@ export function haproxyLogsSpecProvider(context: TutorialContext): TutorialSchem onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network', 'web'], }; } diff --git a/src/plugins/home/server/tutorials/haproxy_metrics/index.ts b/src/plugins/home/server/tutorials/haproxy_metrics/index.ts index a6953808871fa..e37f0ffc4b916 100644 --- a/src/plugins/home/server/tutorials/haproxy_metrics/index.ts +++ b/src/plugins/home/server/tutorials/haproxy_metrics/index.ts @@ -56,5 +56,6 @@ export function haproxyMetricsSpecProvider(context: TutorialContext): TutorialSc onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['network', 'web'], }; } diff --git a/src/plugins/home/server/tutorials/ibmmq_logs/index.ts b/src/plugins/home/server/tutorials/ibmmq_logs/index.ts index a777443c5f9b8..646747d1a49f8 100644 --- a/src/plugins/home/server/tutorials/ibmmq_logs/index.ts +++ b/src/plugins/home/server/tutorials/ibmmq_logs/index.ts @@ -58,5 +58,6 @@ export function ibmmqLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/ibmmq_metrics/index.ts b/src/plugins/home/server/tutorials/ibmmq_metrics/index.ts index 2501aa81f0e30..3862bd9ca85eb 100644 --- a/src/plugins/home/server/tutorials/ibmmq_metrics/index.ts +++ b/src/plugins/home/server/tutorials/ibmmq_metrics/index.ts @@ -57,5 +57,6 @@ export function ibmmqMetricsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/icinga_logs/index.ts b/src/plugins/home/server/tutorials/icinga_logs/index.ts index b55023a77332f..0dae93b70343b 100644 --- a/src/plugins/home/server/tutorials/icinga_logs/index.ts +++ b/src/plugins/home/server/tutorials/icinga_logs/index.ts @@ -59,5 +59,6 @@ export function icingaLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/iis_logs/index.ts b/src/plugins/home/server/tutorials/iis_logs/index.ts index f87e87a6aa427..5393edf6ab148 100644 --- a/src/plugins/home/server/tutorials/iis_logs/index.ts +++ b/src/plugins/home/server/tutorials/iis_logs/index.ts @@ -59,5 +59,6 @@ export function iisLogsSpecProvider(context: TutorialContext): TutorialSchema { onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['web'], }; } diff --git a/src/plugins/home/server/tutorials/iis_metrics/index.ts b/src/plugins/home/server/tutorials/iis_metrics/index.ts index 56408beb2071f..dbfa474dc9c89 100644 --- a/src/plugins/home/server/tutorials/iis_metrics/index.ts +++ b/src/plugins/home/server/tutorials/iis_metrics/index.ts @@ -59,5 +59,6 @@ export function iisMetricsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['web'], }; } diff --git a/src/plugins/home/server/tutorials/imperva_logs/index.ts b/src/plugins/home/server/tutorials/imperva_logs/index.ts index 09ea5cf21ec21..71c3af3809e2e 100644 --- a/src/plugins/home/server/tutorials/imperva_logs/index.ts +++ b/src/plugins/home/server/tutorials/imperva_logs/index.ts @@ -56,5 +56,6 @@ export function impervaLogsSpecProvider(context: TutorialContext): TutorialSchem onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/infoblox_logs/index.ts b/src/plugins/home/server/tutorials/infoblox_logs/index.ts index 62769946067e5..5329444dfa85f 100644 --- a/src/plugins/home/server/tutorials/infoblox_logs/index.ts +++ b/src/plugins/home/server/tutorials/infoblox_logs/index.ts @@ -56,5 +56,6 @@ export function infobloxLogsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network'], }; } diff --git a/src/plugins/home/server/tutorials/iptables_logs/index.ts b/src/plugins/home/server/tutorials/iptables_logs/index.ts index b25604db39c0d..85faf169f8714 100644 --- a/src/plugins/home/server/tutorials/iptables_logs/index.ts +++ b/src/plugins/home/server/tutorials/iptables_logs/index.ts @@ -62,5 +62,6 @@ export function iptablesLogsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/juniper_logs/index.ts b/src/plugins/home/server/tutorials/juniper_logs/index.ts index f13f62d0d92e3..f9174d8a089e0 100644 --- a/src/plugins/home/server/tutorials/juniper_logs/index.ts +++ b/src/plugins/home/server/tutorials/juniper_logs/index.ts @@ -56,5 +56,6 @@ export function juniperLogsSpecProvider(context: TutorialContext): TutorialSchem onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/kafka_logs/index.ts b/src/plugins/home/server/tutorials/kafka_logs/index.ts index 13a4317761a86..5b877cadcbec6 100644 --- a/src/plugins/home/server/tutorials/kafka_logs/index.ts +++ b/src/plugins/home/server/tutorials/kafka_logs/index.ts @@ -59,5 +59,6 @@ export function kafkaLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['message_queue'], }; } diff --git a/src/plugins/home/server/tutorials/kafka_metrics/index.ts b/src/plugins/home/server/tutorials/kafka_metrics/index.ts index b6840cb69ec57..92f6744b91cbe 100644 --- a/src/plugins/home/server/tutorials/kafka_metrics/index.ts +++ b/src/plugins/home/server/tutorials/kafka_metrics/index.ts @@ -56,5 +56,6 @@ export function kafkaMetricsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['message_queue'], }; } diff --git a/src/plugins/home/server/tutorials/kibana_logs/index.ts b/src/plugins/home/server/tutorials/kibana_logs/index.ts index 27207de013dbd..988af821ef9e3 100644 --- a/src/plugins/home/server/tutorials/kibana_logs/index.ts +++ b/src/plugins/home/server/tutorials/kibana_logs/index.ts @@ -55,5 +55,6 @@ export function kibanaLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['message_queue'], }; } diff --git a/src/plugins/home/server/tutorials/kibana_metrics/index.ts b/src/plugins/home/server/tutorials/kibana_metrics/index.ts index 0e5e0c92e77e9..dfe4efe4f7337 100644 --- a/src/plugins/home/server/tutorials/kibana_metrics/index.ts +++ b/src/plugins/home/server/tutorials/kibana_metrics/index.ts @@ -56,5 +56,6 @@ export function kibanaMetricsSpecProvider(context: TutorialContext): TutorialSch onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['message_queue'], }; } diff --git a/src/plugins/home/server/tutorials/kubernetes_metrics/index.ts b/src/plugins/home/server/tutorials/kubernetes_metrics/index.ts index 48bc3c4b69e39..4a694560f5c28 100644 --- a/src/plugins/home/server/tutorials/kubernetes_metrics/index.ts +++ b/src/plugins/home/server/tutorials/kubernetes_metrics/index.ts @@ -61,5 +61,6 @@ export function kubernetesMetricsSpecProvider(context: TutorialContext): Tutoria onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['containers', 'kubernetes'], }; } diff --git a/src/plugins/home/server/tutorials/logstash_logs/index.ts b/src/plugins/home/server/tutorials/logstash_logs/index.ts index e41796b4b57bf..55491d45df28c 100644 --- a/src/plugins/home/server/tutorials/logstash_logs/index.ts +++ b/src/plugins/home/server/tutorials/logstash_logs/index.ts @@ -58,5 +58,6 @@ export function logstashLogsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['custom'], }; } diff --git a/src/plugins/home/server/tutorials/logstash_metrics/index.ts b/src/plugins/home/server/tutorials/logstash_metrics/index.ts index 0aa85745b0fa3..e7d3fae011bd2 100644 --- a/src/plugins/home/server/tutorials/logstash_metrics/index.ts +++ b/src/plugins/home/server/tutorials/logstash_metrics/index.ts @@ -57,5 +57,6 @@ export function logstashMetricsSpecProvider(context: TutorialContext): TutorialS onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['custom'], }; } diff --git a/src/plugins/home/server/tutorials/memcached_metrics/index.ts b/src/plugins/home/server/tutorials/memcached_metrics/index.ts index 5032130eed0b6..15df179b44a9e 100644 --- a/src/plugins/home/server/tutorials/memcached_metrics/index.ts +++ b/src/plugins/home/server/tutorials/memcached_metrics/index.ts @@ -56,5 +56,6 @@ export function memcachedMetricsSpecProvider(context: TutorialContext): Tutorial onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['custom'], }; } diff --git a/src/plugins/home/server/tutorials/microsoft_logs/index.ts b/src/plugins/home/server/tutorials/microsoft_logs/index.ts index ee860845c04b6..52401df1f9eb7 100644 --- a/src/plugins/home/server/tutorials/microsoft_logs/index.ts +++ b/src/plugins/home/server/tutorials/microsoft_logs/index.ts @@ -59,5 +59,6 @@ export function microsoftLogsSpecProvider(context: TutorialContext): TutorialSch onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network', 'security', 'azure'], }; } diff --git a/src/plugins/home/server/tutorials/misp_logs/index.ts b/src/plugins/home/server/tutorials/misp_logs/index.ts index 3c341f5ad1e93..b7611b543bab1 100644 --- a/src/plugins/home/server/tutorials/misp_logs/index.ts +++ b/src/plugins/home/server/tutorials/misp_logs/index.ts @@ -59,5 +59,6 @@ export function mispLogsSpecProvider(context: TutorialContext): TutorialSchema { onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network', 'security', 'azure'], }; } diff --git a/src/plugins/home/server/tutorials/mongodb_logs/index.ts b/src/plugins/home/server/tutorials/mongodb_logs/index.ts index 7b5ee10c1d5e0..3c189c04da43b 100644 --- a/src/plugins/home/server/tutorials/mongodb_logs/index.ts +++ b/src/plugins/home/server/tutorials/mongodb_logs/index.ts @@ -59,5 +59,6 @@ export function mongodbLogsSpecProvider(context: TutorialContext): TutorialSchem onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['datastore'], }; } diff --git a/src/plugins/home/server/tutorials/mongodb_metrics/index.ts b/src/plugins/home/server/tutorials/mongodb_metrics/index.ts index c7946309cb2d8..121310fba6f3a 100644 --- a/src/plugins/home/server/tutorials/mongodb_metrics/index.ts +++ b/src/plugins/home/server/tutorials/mongodb_metrics/index.ts @@ -61,5 +61,6 @@ export function mongodbMetricsSpecProvider(context: TutorialContext): TutorialSc onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['datastore'], }; } diff --git a/src/plugins/home/server/tutorials/mssql_logs/index.ts b/src/plugins/home/server/tutorials/mssql_logs/index.ts index 1b605d2c17dbb..567080910b7fe 100644 --- a/src/plugins/home/server/tutorials/mssql_logs/index.ts +++ b/src/plugins/home/server/tutorials/mssql_logs/index.ts @@ -56,5 +56,6 @@ export function mssqlLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['datastore'], }; } diff --git a/src/plugins/home/server/tutorials/mssql_metrics/index.ts b/src/plugins/home/server/tutorials/mssql_metrics/index.ts index 767612e3a1c2e..998cefe2de004 100644 --- a/src/plugins/home/server/tutorials/mssql_metrics/index.ts +++ b/src/plugins/home/server/tutorials/mssql_metrics/index.ts @@ -59,5 +59,6 @@ export function mssqlMetricsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['datastore'], }; } diff --git a/src/plugins/home/server/tutorials/munin_metrics/index.ts b/src/plugins/home/server/tutorials/munin_metrics/index.ts index 063770e43e6d1..1abd321e4c738 100644 --- a/src/plugins/home/server/tutorials/munin_metrics/index.ts +++ b/src/plugins/home/server/tutorials/munin_metrics/index.ts @@ -56,5 +56,6 @@ export function muninMetricsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['datastore'], }; } diff --git a/src/plugins/home/server/tutorials/mysql_logs/index.ts b/src/plugins/home/server/tutorials/mysql_logs/index.ts index 79eaeb3826162..a788e736d2964 100644 --- a/src/plugins/home/server/tutorials/mysql_logs/index.ts +++ b/src/plugins/home/server/tutorials/mysql_logs/index.ts @@ -59,5 +59,6 @@ export function mysqlLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['datastore'], }; } diff --git a/src/plugins/home/server/tutorials/mysql_metrics/index.ts b/src/plugins/home/server/tutorials/mysql_metrics/index.ts index 66da2d8e0a5ff..078a96f8110df 100644 --- a/src/plugins/home/server/tutorials/mysql_metrics/index.ts +++ b/src/plugins/home/server/tutorials/mysql_metrics/index.ts @@ -58,5 +58,6 @@ export function mysqlMetricsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['datastore'], }; } diff --git a/src/plugins/home/server/tutorials/nats_logs/index.ts b/src/plugins/home/server/tutorials/nats_logs/index.ts index c5c7028e19163..a1dc24080bc0d 100644 --- a/src/plugins/home/server/tutorials/nats_logs/index.ts +++ b/src/plugins/home/server/tutorials/nats_logs/index.ts @@ -60,5 +60,6 @@ export function natsLogsSpecProvider(context: TutorialContext): TutorialSchema { onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['message_queue'], }; } diff --git a/src/plugins/home/server/tutorials/nats_metrics/index.ts b/src/plugins/home/server/tutorials/nats_metrics/index.ts index bf25ab9f8d33e..11494e5dc57d0 100644 --- a/src/plugins/home/server/tutorials/nats_metrics/index.ts +++ b/src/plugins/home/server/tutorials/nats_metrics/index.ts @@ -58,5 +58,6 @@ export function natsMetricsSpecProvider(context: TutorialContext): TutorialSchem onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['message_queue'], }; } diff --git a/src/plugins/home/server/tutorials/netflow_logs/index.ts b/src/plugins/home/server/tutorials/netflow_logs/index.ts index 1a85c3feb1173..e8404e93ae355 100644 --- a/src/plugins/home/server/tutorials/netflow_logs/index.ts +++ b/src/plugins/home/server/tutorials/netflow_logs/index.ts @@ -58,5 +58,6 @@ export function netflowLogsSpecProvider(context: TutorialContext): TutorialSchem onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/netscout_logs/index.ts b/src/plugins/home/server/tutorials/netscout_logs/index.ts index 57429e9acddfe..395fbb8b49d39 100644 --- a/src/plugins/home/server/tutorials/netscout_logs/index.ts +++ b/src/plugins/home/server/tutorials/netscout_logs/index.ts @@ -56,5 +56,6 @@ export function netscoutLogsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/nginx_logs/index.ts b/src/plugins/home/server/tutorials/nginx_logs/index.ts index a1419fbb42d5a..90ec6737c2461 100644 --- a/src/plugins/home/server/tutorials/nginx_logs/index.ts +++ b/src/plugins/home/server/tutorials/nginx_logs/index.ts @@ -59,5 +59,6 @@ export function nginxLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['web', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/nginx_metrics/index.ts b/src/plugins/home/server/tutorials/nginx_metrics/index.ts index 32b20844ef813..12f67a26dcf29 100644 --- a/src/plugins/home/server/tutorials/nginx_metrics/index.ts +++ b/src/plugins/home/server/tutorials/nginx_metrics/index.ts @@ -63,5 +63,6 @@ which must be enabled in your Nginx installation. \ onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['web', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/o365_logs/index.ts b/src/plugins/home/server/tutorials/o365_logs/index.ts index 50cd13298a879..e3663e2c3cd78 100644 --- a/src/plugins/home/server/tutorials/o365_logs/index.ts +++ b/src/plugins/home/server/tutorials/o365_logs/index.ts @@ -62,5 +62,6 @@ export function o365LogsSpecProvider(context: TutorialContext): TutorialSchema { onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/okta_logs/index.ts b/src/plugins/home/server/tutorials/okta_logs/index.ts index ac173c79db605..62cde4b5128c3 100644 --- a/src/plugins/home/server/tutorials/okta_logs/index.ts +++ b/src/plugins/home/server/tutorials/okta_logs/index.ts @@ -60,5 +60,6 @@ export function oktaLogsSpecProvider(context: TutorialContext): TutorialSchema { onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/openmetrics_metrics/index.ts b/src/plugins/home/server/tutorials/openmetrics_metrics/index.ts index 6885c1ccea86d..acbddf5169881 100644 --- a/src/plugins/home/server/tutorials/openmetrics_metrics/index.ts +++ b/src/plugins/home/server/tutorials/openmetrics_metrics/index.ts @@ -49,5 +49,6 @@ export function openmetricsMetricsSpecProvider(context: TutorialContext): Tutori onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/osquery_logs/index.ts b/src/plugins/home/server/tutorials/osquery_logs/index.ts index 40ed13d767319..6bacbed57792c 100644 --- a/src/plugins/home/server/tutorials/osquery_logs/index.ts +++ b/src/plugins/home/server/tutorials/osquery_logs/index.ts @@ -62,5 +62,6 @@ export function osqueryLogsSpecProvider(context: TutorialContext): TutorialSchem onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security', 'os_system'], }; } diff --git a/src/plugins/home/server/tutorials/panw_logs/index.ts b/src/plugins/home/server/tutorials/panw_logs/index.ts index a46b2deb29826..3ca839556d756 100644 --- a/src/plugins/home/server/tutorials/panw_logs/index.ts +++ b/src/plugins/home/server/tutorials/panw_logs/index.ts @@ -62,5 +62,6 @@ export function panwLogsSpecProvider(context: TutorialContext): TutorialSchema { onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/php_fpm_metrics/index.ts b/src/plugins/home/server/tutorials/php_fpm_metrics/index.ts index 229a167bb137e..ed67960ab5a1c 100644 --- a/src/plugins/home/server/tutorials/php_fpm_metrics/index.ts +++ b/src/plugins/home/server/tutorials/php_fpm_metrics/index.ts @@ -56,5 +56,6 @@ export function phpfpmMetricsSpecProvider(context: TutorialContext): TutorialSch onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/postgresql_logs/index.ts b/src/plugins/home/server/tutorials/postgresql_logs/index.ts index da7b8927a610d..c5f5d879ac35d 100644 --- a/src/plugins/home/server/tutorials/postgresql_logs/index.ts +++ b/src/plugins/home/server/tutorials/postgresql_logs/index.ts @@ -62,5 +62,6 @@ export function postgresqlLogsSpecProvider(context: TutorialContext): TutorialSc onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['datastore'], }; } diff --git a/src/plugins/home/server/tutorials/postgresql_metrics/index.ts b/src/plugins/home/server/tutorials/postgresql_metrics/index.ts index eb5ed17021d1a..ca20efb44bca7 100644 --- a/src/plugins/home/server/tutorials/postgresql_metrics/index.ts +++ b/src/plugins/home/server/tutorials/postgresql_metrics/index.ts @@ -58,5 +58,6 @@ export function postgresqlMetricsSpecProvider(context: TutorialContext): Tutoria onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['datastore'], }; } diff --git a/src/plugins/home/server/tutorials/rabbitmq_logs/index.ts b/src/plugins/home/server/tutorials/rabbitmq_logs/index.ts index 92e6623da6f53..0fbdb48236832 100644 --- a/src/plugins/home/server/tutorials/rabbitmq_logs/index.ts +++ b/src/plugins/home/server/tutorials/rabbitmq_logs/index.ts @@ -56,5 +56,6 @@ export function rabbitmqLogsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['message_queue'], }; } diff --git a/src/plugins/home/server/tutorials/rabbitmq_metrics/index.ts b/src/plugins/home/server/tutorials/rabbitmq_metrics/index.ts index 56b7a8c8c2109..b58f936f205b2 100644 --- a/src/plugins/home/server/tutorials/rabbitmq_metrics/index.ts +++ b/src/plugins/home/server/tutorials/rabbitmq_metrics/index.ts @@ -62,5 +62,6 @@ export function rabbitmqMetricsSpecProvider(context: TutorialContext): TutorialS onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['message_queue'], }; } diff --git a/src/plugins/home/server/tutorials/radware_logs/index.ts b/src/plugins/home/server/tutorials/radware_logs/index.ts index 3d91514c2c13e..28392cf9c4362 100644 --- a/src/plugins/home/server/tutorials/radware_logs/index.ts +++ b/src/plugins/home/server/tutorials/radware_logs/index.ts @@ -56,5 +56,6 @@ export function radwareLogsSpecProvider(context: TutorialContext): TutorialSchem onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/redis_logs/index.ts b/src/plugins/home/server/tutorials/redis_logs/index.ts index 116099e809832..0f3a5aa812f49 100644 --- a/src/plugins/home/server/tutorials/redis_logs/index.ts +++ b/src/plugins/home/server/tutorials/redis_logs/index.ts @@ -65,5 +65,6 @@ Note that the `slowlog` fileset is experimental. \ onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['datastore', 'message_queue'], }; } diff --git a/src/plugins/home/server/tutorials/redis_metrics/index.ts b/src/plugins/home/server/tutorials/redis_metrics/index.ts index bce77a2b18cc3..1b4ee7290a6d0 100644 --- a/src/plugins/home/server/tutorials/redis_metrics/index.ts +++ b/src/plugins/home/server/tutorials/redis_metrics/index.ts @@ -58,5 +58,6 @@ export function redisMetricsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['datastore', 'message_queue'], }; } diff --git a/src/plugins/home/server/tutorials/redisenterprise_metrics/index.ts b/src/plugins/home/server/tutorials/redisenterprise_metrics/index.ts index 257440ec69987..be8de9c3eab4d 100644 --- a/src/plugins/home/server/tutorials/redisenterprise_metrics/index.ts +++ b/src/plugins/home/server/tutorials/redisenterprise_metrics/index.ts @@ -57,5 +57,6 @@ export function redisenterpriseMetricsSpecProvider(context: TutorialContext): Tu onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['datastore', 'message_queue'], }; } diff --git a/src/plugins/home/server/tutorials/santa_logs/index.ts b/src/plugins/home/server/tutorials/santa_logs/index.ts index fb5ba7ede4171..10d1506438b62 100644 --- a/src/plugins/home/server/tutorials/santa_logs/index.ts +++ b/src/plugins/home/server/tutorials/santa_logs/index.ts @@ -60,5 +60,6 @@ export function santaLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security', 'os_system'], }; } diff --git a/src/plugins/home/server/tutorials/sonicwall_logs/index.ts b/src/plugins/home/server/tutorials/sonicwall_logs/index.ts index fa33f81728db7..1fa711327a07d 100644 --- a/src/plugins/home/server/tutorials/sonicwall_logs/index.ts +++ b/src/plugins/home/server/tutorials/sonicwall_logs/index.ts @@ -56,5 +56,6 @@ export function sonicwallLogsSpecProvider(context: TutorialContext): TutorialSch onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/sophos_logs/index.ts b/src/plugins/home/server/tutorials/sophos_logs/index.ts index 19b6b3033c9f1..35b27973a55ec 100644 --- a/src/plugins/home/server/tutorials/sophos_logs/index.ts +++ b/src/plugins/home/server/tutorials/sophos_logs/index.ts @@ -56,5 +56,6 @@ export function sophosLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/squid_logs/index.ts b/src/plugins/home/server/tutorials/squid_logs/index.ts index ef6ea4aef3e17..d8d0bb6c0829b 100644 --- a/src/plugins/home/server/tutorials/squid_logs/index.ts +++ b/src/plugins/home/server/tutorials/squid_logs/index.ts @@ -56,5 +56,6 @@ export function squidLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['security'], }; } diff --git a/src/plugins/home/server/tutorials/stan_metrics/index.ts b/src/plugins/home/server/tutorials/stan_metrics/index.ts index 10b7fc08bbf19..ceb6084b539e6 100644 --- a/src/plugins/home/server/tutorials/stan_metrics/index.ts +++ b/src/plugins/home/server/tutorials/stan_metrics/index.ts @@ -58,5 +58,6 @@ export function stanMetricsSpecProvider(context: TutorialContext): TutorialSchem onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['message_queue', 'kubernetes'], }; } diff --git a/src/plugins/home/server/tutorials/statsd_metrics/index.ts b/src/plugins/home/server/tutorials/statsd_metrics/index.ts index 70a35d3f6f648..472c1406db386 100644 --- a/src/plugins/home/server/tutorials/statsd_metrics/index.ts +++ b/src/plugins/home/server/tutorials/statsd_metrics/index.ts @@ -47,5 +47,6 @@ export function statsdMetricsSpecProvider(context: TutorialContext): TutorialSch onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['message_queue', 'kubernetes'], }; } diff --git a/src/plugins/home/server/tutorials/suricata_logs/index.ts b/src/plugins/home/server/tutorials/suricata_logs/index.ts index c06c7c05c5583..3bb2b93b6301a 100644 --- a/src/plugins/home/server/tutorials/suricata_logs/index.ts +++ b/src/plugins/home/server/tutorials/suricata_logs/index.ts @@ -60,5 +60,6 @@ export function suricataLogsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/system_logs/index.ts b/src/plugins/home/server/tutorials/system_logs/index.ts index 087f8e04e250d..6f403a6d0a71a 100644 --- a/src/plugins/home/server/tutorials/system_logs/index.ts +++ b/src/plugins/home/server/tutorials/system_logs/index.ts @@ -58,5 +58,6 @@ export function systemLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['os_system', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/system_metrics/index.ts b/src/plugins/home/server/tutorials/system_metrics/index.ts index 91e0b74a05d68..08979a3d3b003 100644 --- a/src/plugins/home/server/tutorials/system_metrics/index.ts +++ b/src/plugins/home/server/tutorials/system_metrics/index.ts @@ -59,5 +59,6 @@ It collects system wide statistics and statistics per process and filesystem. \ onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['os_system', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/tomcat_logs/index.ts b/src/plugins/home/server/tutorials/tomcat_logs/index.ts index 4632f5d64eec8..5ce4096ad4628 100644 --- a/src/plugins/home/server/tutorials/tomcat_logs/index.ts +++ b/src/plugins/home/server/tutorials/tomcat_logs/index.ts @@ -56,5 +56,6 @@ export function tomcatLogsSpecProvider(context: TutorialContext): TutorialSchema onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['web', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/traefik_logs/index.ts b/src/plugins/home/server/tutorials/traefik_logs/index.ts index 139a158d77b81..6bbc905bbd6aa 100644 --- a/src/plugins/home/server/tutorials/traefik_logs/index.ts +++ b/src/plugins/home/server/tutorials/traefik_logs/index.ts @@ -58,5 +58,6 @@ export function traefikLogsSpecProvider(context: TutorialContext): TutorialSchem onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['web', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/traefik_metrics/index.ts b/src/plugins/home/server/tutorials/traefik_metrics/index.ts index a036de3546ba8..35d54317c8ede 100644 --- a/src/plugins/home/server/tutorials/traefik_metrics/index.ts +++ b/src/plugins/home/server/tutorials/traefik_metrics/index.ts @@ -46,5 +46,6 @@ export function traefikMetricsSpecProvider(context: TutorialContext): TutorialSc onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['web', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/uptime_monitors/index.ts b/src/plugins/home/server/tutorials/uptime_monitors/index.ts index f04e0e1e797ef..6e949d5410115 100644 --- a/src/plugins/home/server/tutorials/uptime_monitors/index.ts +++ b/src/plugins/home/server/tutorials/uptime_monitors/index.ts @@ -57,5 +57,6 @@ export function uptimeMonitorsSpecProvider(context: TutorialContext): TutorialSc onPrem: onPremInstructions([], context), elasticCloud: cloudInstructions(), onPremElasticCloud: onPremCloudInstructions(), + integrationBrowserCategories: ['web', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/uwsgi_metrics/index.ts b/src/plugins/home/server/tutorials/uwsgi_metrics/index.ts index d41bdad6a90d9..d9cfcc9f7fb75 100644 --- a/src/plugins/home/server/tutorials/uwsgi_metrics/index.ts +++ b/src/plugins/home/server/tutorials/uwsgi_metrics/index.ts @@ -59,5 +59,6 @@ export function uwsgiMetricsSpecProvider(context: TutorialContext): TutorialSche onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['web', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/vsphere_metrics/index.ts b/src/plugins/home/server/tutorials/vsphere_metrics/index.ts index b4786d060ecd1..bcbcec59c36e4 100644 --- a/src/plugins/home/server/tutorials/vsphere_metrics/index.ts +++ b/src/plugins/home/server/tutorials/vsphere_metrics/index.ts @@ -56,5 +56,6 @@ export function vSphereMetricsSpecProvider(context: TutorialContext): TutorialSc onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['web', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/windows_event_logs/index.ts b/src/plugins/home/server/tutorials/windows_event_logs/index.ts index 5b073e7a0fcde..0df7fa906e085 100644 --- a/src/plugins/home/server/tutorials/windows_event_logs/index.ts +++ b/src/plugins/home/server/tutorials/windows_event_logs/index.ts @@ -56,5 +56,6 @@ export function windowsEventLogsSpecProvider(context: TutorialContext): Tutorial onPrem: onPremInstructions(context), elasticCloud: cloudInstructions(), onPremElasticCloud: onPremCloudInstructions(), + integrationBrowserCategories: ['os_system', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/windows_metrics/index.ts b/src/plugins/home/server/tutorials/windows_metrics/index.ts index e937a962f1bd5..6c663fbb13d4d 100644 --- a/src/plugins/home/server/tutorials/windows_metrics/index.ts +++ b/src/plugins/home/server/tutorials/windows_metrics/index.ts @@ -56,5 +56,6 @@ export function windowsMetricsSpecProvider(context: TutorialContext): TutorialSc onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['os_system', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/zeek_logs/index.ts b/src/plugins/home/server/tutorials/zeek_logs/index.ts index 9c5baa8368a7c..5434dcc8527ff 100644 --- a/src/plugins/home/server/tutorials/zeek_logs/index.ts +++ b/src/plugins/home/server/tutorials/zeek_logs/index.ts @@ -60,5 +60,6 @@ export function zeekLogsSpecProvider(context: TutorialContext): TutorialSchema { onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network', 'monitoring', 'security'], }; } diff --git a/src/plugins/home/server/tutorials/zookeeper_metrics/index.ts b/src/plugins/home/server/tutorials/zookeeper_metrics/index.ts index 29038269b0825..85ca03acacfd4 100644 --- a/src/plugins/home/server/tutorials/zookeeper_metrics/index.ts +++ b/src/plugins/home/server/tutorials/zookeeper_metrics/index.ts @@ -57,5 +57,6 @@ export function zookeeperMetricsSpecProvider(context: TutorialContext): Tutorial onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), + integrationBrowserCategories: ['datastore', 'config_management'], }; } diff --git a/src/plugins/home/server/tutorials/zscaler_logs/index.ts b/src/plugins/home/server/tutorials/zscaler_logs/index.ts index fcfcccf2c50bf..a2eb41a257a92 100644 --- a/src/plugins/home/server/tutorials/zscaler_logs/index.ts +++ b/src/plugins/home/server/tutorials/zscaler_logs/index.ts @@ -56,5 +56,6 @@ export function zscalerLogsSpecProvider(context: TutorialContext): TutorialSchem onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), + integrationBrowserCategories: ['network', 'security'], }; } From b91f8c8679d250f563d6f30f58d213a5b82bce88 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 15:32:08 -0400 Subject: [PATCH 44/51] [logging] Upgrade ECS to 1.12 (#113583) (#113651) Co-authored-by: Luke Elmers --- packages/kbn-logging/src/ecs/agent.ts | 2 +- .../kbn-logging/src/ecs/autonomous_system.ts | 2 +- packages/kbn-logging/src/ecs/base.ts | 2 +- packages/kbn-logging/src/ecs/client.ts | 2 +- packages/kbn-logging/src/ecs/cloud.ts | 2 +- .../kbn-logging/src/ecs/code_signature.ts | 4 +- packages/kbn-logging/src/ecs/container.ts | 9 ++- packages/kbn-logging/src/ecs/data_stream.ts | 18 +++++ packages/kbn-logging/src/ecs/destination.ts | 2 +- packages/kbn-logging/src/ecs/dll.ts | 2 +- packages/kbn-logging/src/ecs/dns.ts | 2 +- packages/kbn-logging/src/ecs/elf.ts | 71 +++++++++++++++++++ packages/kbn-logging/src/ecs/email.ts | 48 +++++++++++++ packages/kbn-logging/src/ecs/error.ts | 2 +- packages/kbn-logging/src/ecs/event.ts | 3 +- packages/kbn-logging/src/ecs/file.ts | 5 +- packages/kbn-logging/src/ecs/geo.ts | 2 +- packages/kbn-logging/src/ecs/group.ts | 2 +- packages/kbn-logging/src/ecs/hash.ts | 2 +- packages/kbn-logging/src/ecs/host.ts | 2 +- packages/kbn-logging/src/ecs/http.ts | 2 +- packages/kbn-logging/src/ecs/index.ts | 12 +++- packages/kbn-logging/src/ecs/interface.ts | 2 +- packages/kbn-logging/src/ecs/log.ts | 3 +- packages/kbn-logging/src/ecs/network.ts | 2 +- packages/kbn-logging/src/ecs/observer.ts | 2 +- packages/kbn-logging/src/ecs/orchestrator.ts | 32 +++++++++ packages/kbn-logging/src/ecs/organization.ts | 2 +- packages/kbn-logging/src/ecs/os.ts | 2 +- packages/kbn-logging/src/ecs/package.ts | 2 +- packages/kbn-logging/src/ecs/pe.ts | 2 +- packages/kbn-logging/src/ecs/process.ts | 7 +- packages/kbn-logging/src/ecs/registry.ts | 2 +- packages/kbn-logging/src/ecs/related.ts | 2 +- packages/kbn-logging/src/ecs/rule.ts | 2 +- packages/kbn-logging/src/ecs/server.ts | 2 +- packages/kbn-logging/src/ecs/service.ts | 4 +- packages/kbn-logging/src/ecs/source.ts | 2 +- packages/kbn-logging/src/ecs/threat.ts | 67 ++++++++++++++++- packages/kbn-logging/src/ecs/tls.ts | 2 +- packages/kbn-logging/src/ecs/tracing.ts | 2 +- packages/kbn-logging/src/ecs/url.ts | 2 +- packages/kbn-logging/src/ecs/user.ts | 2 +- packages/kbn-logging/src/ecs/user_agent.ts | 2 +- packages/kbn-logging/src/ecs/vlan.ts | 2 +- packages/kbn-logging/src/ecs/vulnerability.ts | 2 +- packages/kbn-logging/src/ecs/x509.ts | 2 +- .../__snapshots__/logging_system.test.ts.snap | 10 +-- .../__snapshots__/json_layout.test.ts.snap | 12 ++-- .../logging/layouts/json_layout.test.ts | 16 ++--- .../server/logging/layouts/json_layout.ts | 2 +- .../security/server/audit/audit_events.ts | 2 +- 52 files changed, 326 insertions(+), 67 deletions(-) create mode 100644 packages/kbn-logging/src/ecs/data_stream.ts create mode 100644 packages/kbn-logging/src/ecs/elf.ts create mode 100644 packages/kbn-logging/src/ecs/email.ts create mode 100644 packages/kbn-logging/src/ecs/orchestrator.ts diff --git a/packages/kbn-logging/src/ecs/agent.ts b/packages/kbn-logging/src/ecs/agent.ts index 0c2e7f7bbe44f..fc01fc649d266 100644 --- a/packages/kbn-logging/src/ecs/agent.ts +++ b/packages/kbn-logging/src/ecs/agent.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-agent.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-agent.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/autonomous_system.ts b/packages/kbn-logging/src/ecs/autonomous_system.ts index 85569b7dbabe1..5e59cb84e198b 100644 --- a/packages/kbn-logging/src/ecs/autonomous_system.ts +++ b/packages/kbn-logging/src/ecs/autonomous_system.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-as.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-as.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/base.ts b/packages/kbn-logging/src/ecs/base.ts index cf12cf0ea6e53..95c67623869f5 100644 --- a/packages/kbn-logging/src/ecs/base.ts +++ b/packages/kbn-logging/src/ecs/base.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-base.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-base.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/client.ts b/packages/kbn-logging/src/ecs/client.ts index ebee7826104a5..ef4b227593e62 100644 --- a/packages/kbn-logging/src/ecs/client.ts +++ b/packages/kbn-logging/src/ecs/client.ts @@ -17,7 +17,7 @@ interface NestedFields { } /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-client.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-client.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/cloud.ts b/packages/kbn-logging/src/ecs/cloud.ts index 8ef15d40f5529..803fc63bef216 100644 --- a/packages/kbn-logging/src/ecs/cloud.ts +++ b/packages/kbn-logging/src/ecs/cloud.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-cloud.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-cloud.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/code_signature.ts b/packages/kbn-logging/src/ecs/code_signature.ts index 277c3901a4f8b..e2e5eaf5c89af 100644 --- a/packages/kbn-logging/src/ecs/code_signature.ts +++ b/packages/kbn-logging/src/ecs/code_signature.ts @@ -7,15 +7,17 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-code_signature.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-code_signature.html * * @internal */ export interface EcsCodeSignature { + digest_algorithm?: string; exists?: boolean; signing_id?: string; status?: string; subject_name?: string; + timestamp?: string; team_id?: string; trusted?: boolean; valid?: boolean; diff --git a/packages/kbn-logging/src/ecs/container.ts b/packages/kbn-logging/src/ecs/container.ts index 6c5c85e7107e3..746b5b917a745 100644 --- a/packages/kbn-logging/src/ecs/container.ts +++ b/packages/kbn-logging/src/ecs/container.ts @@ -7,14 +7,21 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-container.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-container.html * * @internal */ export interface EcsContainer { + cpu?: { usage?: number }; + disk?: Disk; id?: string; image?: { name?: string; tag?: string[] }; labels?: Record; name?: string; runtime?: string; } + +interface Disk { + read?: { bytes?: number }; + write?: { bytes?: number }; +} diff --git a/packages/kbn-logging/src/ecs/data_stream.ts b/packages/kbn-logging/src/ecs/data_stream.ts new file mode 100644 index 0000000000000..fae0a30f69cc4 --- /dev/null +++ b/packages/kbn-logging/src/ecs/data_stream.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 + * 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. + */ + +/** + * https://www.elastic.co/guide/en/ecs/1.12/ecs-data_stream.html + * + * @internal + */ +export interface EcsDataStream { + dataset?: string; + namespace?: string; + type?: 'logs' | 'metrics'; +} diff --git a/packages/kbn-logging/src/ecs/destination.ts b/packages/kbn-logging/src/ecs/destination.ts index 6d2dbc8f431c9..00473da4a1e78 100644 --- a/packages/kbn-logging/src/ecs/destination.ts +++ b/packages/kbn-logging/src/ecs/destination.ts @@ -17,7 +17,7 @@ interface NestedFields { } /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-destination.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-destination.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/dll.ts b/packages/kbn-logging/src/ecs/dll.ts index d9ffa68b3f1a5..2983361f753e7 100644 --- a/packages/kbn-logging/src/ecs/dll.ts +++ b/packages/kbn-logging/src/ecs/dll.ts @@ -17,7 +17,7 @@ interface NestedFields { } /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-dll.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-dll.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/dns.ts b/packages/kbn-logging/src/ecs/dns.ts index c7a0e7983376c..c45f8f6b91fc5 100644 --- a/packages/kbn-logging/src/ecs/dns.ts +++ b/packages/kbn-logging/src/ecs/dns.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-dns.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-dns.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/elf.ts b/packages/kbn-logging/src/ecs/elf.ts new file mode 100644 index 0000000000000..a1cb2a85038e6 --- /dev/null +++ b/packages/kbn-logging/src/ecs/elf.ts @@ -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. + */ + +/** + * https://www.elastic.co/guide/en/ecs/1.12/ecs-elf.html + * + * @internal + */ +export interface EcsElf { + architecture?: string; + byte_order?: string; + cpu_type?: string; + creation_date?: string; + exports?: Export[]; + imports?: Import[]; + header?: Header; + sections?: Section[]; + segments?: Segment[]; + shared_libraries?: string[]; + telfhash?: string; +} + +interface Export { + binding?: string; + name?: string; + section?: string; + size?: string; + type?: string; + version?: string; + visibility?: string; +} + +interface Import { + library?: string; + name?: string; + type?: string; + version?: string; +} + +interface Header { + abi_version?: string; + class?: string; + data?: string; + entrypoint?: number; + object_version?: string; + os_abi?: string; + type?: string; + version?: string; +} + +interface Section { + chi2?: number; + entropy?: number; + flags?: string; + name?: string; + physical_offset?: string; + physical_size?: number; + type?: string; + virtual_address?: number; + virtual_size?: number; +} + +interface Segment { + sections?: string; + type?: string; +} diff --git a/packages/kbn-logging/src/ecs/email.ts b/packages/kbn-logging/src/ecs/email.ts new file mode 100644 index 0000000000000..fcc3295fd71b5 --- /dev/null +++ b/packages/kbn-logging/src/ecs/email.ts @@ -0,0 +1,48 @@ +/* + * 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 { EcsFile } from './file'; +import { EcsHash } from './hash'; + +interface NestedFields { + // Not all hash types are explicitly supported, see + // https://github.com/elastic/ecs/pull/1569 + hash?: Pick; +} + +interface AttachmentNestedFields { + file?: Pick; +} + +/** + * No docs yet, see https://github.com/elastic/ecs/pull/1569 + * + * @internal + */ +export interface EcsEmail extends NestedFields { + attachments?: Attachment[]; + bcc?: string[]; + cc?: string[]; + content_type?: string; + delivery_timestamp?: string; + direction?: string; + from?: string; + local_id?: string; + message_id?: string; + origination_timestamp?: string; + reply_to?: string; + subject?: string; + 'subject.text'?: string; + to?: string[]; + x_mailer?: string; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface Attachment extends AttachmentNestedFields { + // intentionally empty +} diff --git a/packages/kbn-logging/src/ecs/error.ts b/packages/kbn-logging/src/ecs/error.ts index aee010748ddf2..a48b03075d469 100644 --- a/packages/kbn-logging/src/ecs/error.ts +++ b/packages/kbn-logging/src/ecs/error.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-error.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-error.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/event.ts b/packages/kbn-logging/src/ecs/event.ts index bf711410a9dd7..2baf320de8db4 100644 --- a/packages/kbn-logging/src/ecs/event.ts +++ b/packages/kbn-logging/src/ecs/event.ts @@ -7,12 +7,13 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-event.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-event.html * * @internal */ export interface EcsEvent { action?: string; + agent_id_status?: 'verified' | 'mismatch' | 'missing' | 'auth_metadata_missing'; category?: EcsEventCategory[]; code?: string; created?: string; diff --git a/packages/kbn-logging/src/ecs/file.ts b/packages/kbn-logging/src/ecs/file.ts index c09121607e0a4..dbd1653e21190 100644 --- a/packages/kbn-logging/src/ecs/file.ts +++ b/packages/kbn-logging/src/ecs/file.ts @@ -7,19 +7,21 @@ */ import { EcsCodeSignature } from './code_signature'; +import { EcsElf } from './elf'; import { EcsHash } from './hash'; import { EcsPe } from './pe'; import { EcsX509 } from './x509'; interface NestedFields { code_signature?: EcsCodeSignature; + elf?: EcsElf; hash?: EcsHash; pe?: EcsPe; x509?: EcsX509; } /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-file.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-file.html * * @internal */ @@ -32,6 +34,7 @@ export interface EcsFile extends NestedFields { directory?: string; drive_letter?: string; extension?: string; + fork_name?: string; gid?: string; group?: string; inode?: string; diff --git a/packages/kbn-logging/src/ecs/geo.ts b/packages/kbn-logging/src/ecs/geo.ts index 85d45ca803aee..2373f4f119dfc 100644 --- a/packages/kbn-logging/src/ecs/geo.ts +++ b/packages/kbn-logging/src/ecs/geo.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-geo.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-geo.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/group.ts b/packages/kbn-logging/src/ecs/group.ts index e1bc339964fc0..c0d007d3bea0a 100644 --- a/packages/kbn-logging/src/ecs/group.ts +++ b/packages/kbn-logging/src/ecs/group.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-group.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-group.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/hash.ts b/packages/kbn-logging/src/ecs/hash.ts index 2ecd49f1ca092..fcf134349d7a4 100644 --- a/packages/kbn-logging/src/ecs/hash.ts +++ b/packages/kbn-logging/src/ecs/hash.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-hash.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-hash.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/host.ts b/packages/kbn-logging/src/ecs/host.ts index 085db30e13e7e..6c5046ef458e2 100644 --- a/packages/kbn-logging/src/ecs/host.ts +++ b/packages/kbn-logging/src/ecs/host.ts @@ -18,7 +18,7 @@ interface NestedFields { } /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-host.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-host.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/http.ts b/packages/kbn-logging/src/ecs/http.ts index c734c93318f5c..914e35c0f1fd7 100644 --- a/packages/kbn-logging/src/ecs/http.ts +++ b/packages/kbn-logging/src/ecs/http.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-http.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-http.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/index.ts b/packages/kbn-logging/src/ecs/index.ts index 30da3baa43b72..613945d22ad80 100644 --- a/packages/kbn-logging/src/ecs/index.ts +++ b/packages/kbn-logging/src/ecs/index.ts @@ -13,8 +13,10 @@ import { EcsAutonomousSystem } from './autonomous_system'; import { EcsClient } from './client'; import { EcsCloud } from './cloud'; import { EcsContainer } from './container'; +import { EcsDataStream } from './data_stream'; import { EcsDestination } from './destination'; import { EcsDns } from './dns'; +import { EcsEmail } from './email'; import { EcsError } from './error'; import { EcsEvent } from './event'; import { EcsFile } from './file'; @@ -24,6 +26,7 @@ import { EcsHttp } from './http'; import { EcsLog } from './log'; import { EcsNetwork } from './network'; import { EcsObserver } from './observer'; +import { EcsOrchestrator } from './orchestrator'; import { EcsOrganization } from './organization'; import { EcsPackage } from './package'; import { EcsProcess } from './process'; @@ -45,13 +48,13 @@ export { EcsEventCategory, EcsEventKind, EcsEventOutcome, EcsEventType } from '. interface EcsField { /** - * These typings were written as of ECS 1.9.0. + * These typings were written as of ECS 1.12.0. * Don't change this value without checking the rest * of the types to conform to that ECS version. * - * https://www.elastic.co/guide/en/ecs/1.9/index.html + * https://www.elastic.co/guide/en/ecs/1.12/index.html */ - version: '1.9.0'; + version: '1.12.0'; } /** @@ -68,8 +71,10 @@ export type Ecs = EcsBase & client?: EcsClient; cloud?: EcsCloud; container?: EcsContainer; + data_stream?: EcsDataStream; destination?: EcsDestination; dns?: EcsDns; + email?: EcsEmail; error?: EcsError; event?: EcsEvent; file?: EcsFile; @@ -79,6 +84,7 @@ export type Ecs = EcsBase & log?: EcsLog; network?: EcsNetwork; observer?: EcsObserver; + orchestrator?: EcsOrchestrator; organization?: EcsOrganization; package?: EcsPackage; process?: EcsProcess; diff --git a/packages/kbn-logging/src/ecs/interface.ts b/packages/kbn-logging/src/ecs/interface.ts index 49b33e8338184..80c00fe8d8a12 100644 --- a/packages/kbn-logging/src/ecs/interface.ts +++ b/packages/kbn-logging/src/ecs/interface.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-interface.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-interface.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/log.ts b/packages/kbn-logging/src/ecs/log.ts index 8bc2e4982e96c..e55915094e528 100644 --- a/packages/kbn-logging/src/ecs/log.ts +++ b/packages/kbn-logging/src/ecs/log.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-log.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-log.html * * @internal */ @@ -16,6 +16,7 @@ export interface EcsLog { level?: string; logger?: string; origin?: Origin; + /** @deprecated - use `event.original` instead */ original?: string; syslog?: Syslog; } diff --git a/packages/kbn-logging/src/ecs/network.ts b/packages/kbn-logging/src/ecs/network.ts index 912427b6cdb7e..6d00aa10a9968 100644 --- a/packages/kbn-logging/src/ecs/network.ts +++ b/packages/kbn-logging/src/ecs/network.ts @@ -14,7 +14,7 @@ interface NestedFields { } /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-network.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-network.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/observer.ts b/packages/kbn-logging/src/ecs/observer.ts index be2636d15dcdf..89e1cbb59880c 100644 --- a/packages/kbn-logging/src/ecs/observer.ts +++ b/packages/kbn-logging/src/ecs/observer.ts @@ -29,7 +29,7 @@ interface NestedIngressFields { } /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-observer.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-observer.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/orchestrator.ts b/packages/kbn-logging/src/ecs/orchestrator.ts new file mode 100644 index 0000000000000..ac92006091a93 --- /dev/null +++ b/packages/kbn-logging/src/ecs/orchestrator.ts @@ -0,0 +1,32 @@ +/* + * 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. + */ + +/** + * https://www.elastic.co/guide/en/ecs/1.12/ecs-orchestrator.html + * + * @internal + */ +export interface EcsOrchestrator { + api_version?: string; + cluster?: Cluster; + namespace?: string; + organization?: string; + resource?: Resource; + type?: string; +} + +interface Cluster { + name?: string; + url?: string; + version?: string; +} + +interface Resource { + name?: string; + type?: string; +} diff --git a/packages/kbn-logging/src/ecs/organization.ts b/packages/kbn-logging/src/ecs/organization.ts index 370e6b2646a2f..e9274f864b7d2 100644 --- a/packages/kbn-logging/src/ecs/organization.ts +++ b/packages/kbn-logging/src/ecs/organization.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-organization.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-organization.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/os.ts b/packages/kbn-logging/src/ecs/os.ts index 342eb14264fd3..76690bffaf69f 100644 --- a/packages/kbn-logging/src/ecs/os.ts +++ b/packages/kbn-logging/src/ecs/os.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-os.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-os.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/package.ts b/packages/kbn-logging/src/ecs/package.ts index 10528066f3f29..f0e75b3cd2753 100644 --- a/packages/kbn-logging/src/ecs/package.ts +++ b/packages/kbn-logging/src/ecs/package.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-package.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-package.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/pe.ts b/packages/kbn-logging/src/ecs/pe.ts index bd53b7048a50d..c017dfa87fb23 100644 --- a/packages/kbn-logging/src/ecs/pe.ts +++ b/packages/kbn-logging/src/ecs/pe.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-pe.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-pe.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/process.ts b/packages/kbn-logging/src/ecs/process.ts index 9a034c30fd531..4e527d0b21d4c 100644 --- a/packages/kbn-logging/src/ecs/process.ts +++ b/packages/kbn-logging/src/ecs/process.ts @@ -7,18 +7,21 @@ */ import { EcsCodeSignature } from './code_signature'; +import { EcsElf } from './elf'; import { EcsHash } from './hash'; import { EcsPe } from './pe'; interface NestedFields { code_signature?: EcsCodeSignature; + elf?: EcsElf; hash?: EcsHash; parent?: EcsProcess; pe?: EcsPe; + target?: EcsProcess; } /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-process.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-process.html * * @internal */ @@ -26,6 +29,7 @@ export interface EcsProcess extends NestedFields { args?: string[]; args_count?: number; command_line?: string; + end?: string; entity_id?: string; executable?: string; exit_code?: number; @@ -34,7 +38,6 @@ export interface EcsProcess extends NestedFields { pid?: number; ppid?: number; start?: string; - thread?: { id?: number; name?: string }; title?: string; uptime?: number; working_directory?: string; diff --git a/packages/kbn-logging/src/ecs/registry.ts b/packages/kbn-logging/src/ecs/registry.ts index ba7ef699e2cdb..8cc12de6c136e 100644 --- a/packages/kbn-logging/src/ecs/registry.ts +++ b/packages/kbn-logging/src/ecs/registry.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-registry.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-registry.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/related.ts b/packages/kbn-logging/src/ecs/related.ts index 33c3ff50540ce..6616780177d16 100644 --- a/packages/kbn-logging/src/ecs/related.ts +++ b/packages/kbn-logging/src/ecs/related.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-related.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-related.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/rule.ts b/packages/kbn-logging/src/ecs/rule.ts index c6bf1ce96552a..5a961302de918 100644 --- a/packages/kbn-logging/src/ecs/rule.ts +++ b/packages/kbn-logging/src/ecs/rule.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-rule.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-rule.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/server.ts b/packages/kbn-logging/src/ecs/server.ts index 9b2a9b1a11b42..01b2394c6bccc 100644 --- a/packages/kbn-logging/src/ecs/server.ts +++ b/packages/kbn-logging/src/ecs/server.ts @@ -17,7 +17,7 @@ interface NestedFields { } /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-server.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-server.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/service.ts b/packages/kbn-logging/src/ecs/service.ts index 4cd79e928c076..04cff203da961 100644 --- a/packages/kbn-logging/src/ecs/service.ts +++ b/packages/kbn-logging/src/ecs/service.ts @@ -7,11 +7,13 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-service.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-service.html * * @internal */ export interface EcsService { + address?: string; + environment?: string; ephemeral_id?: string; id?: string; name?: string; diff --git a/packages/kbn-logging/src/ecs/source.ts b/packages/kbn-logging/src/ecs/source.ts index 9ec7e2521d0b9..2879adb0d996e 100644 --- a/packages/kbn-logging/src/ecs/source.ts +++ b/packages/kbn-logging/src/ecs/source.ts @@ -17,7 +17,7 @@ interface NestedFields { } /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-source.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-source.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/threat.ts b/packages/kbn-logging/src/ecs/threat.ts index ac6033949fccd..78adf583b0ee2 100644 --- a/packages/kbn-logging/src/ecs/threat.ts +++ b/packages/kbn-logging/src/ecs/threat.ts @@ -6,17 +6,82 @@ * Side Public License, v 1. */ +import { EcsAutonomousSystem } from './autonomous_system'; +import { EcsFile } from './file'; +import { EcsGeo } from './geo'; +import { EcsRegistry } from './registry'; +import { EcsUrl } from './url'; +import { EcsX509 } from './x509'; + +interface IndicatorNestedFields { + as?: EcsAutonomousSystem; + file?: EcsFile; + geo?: EcsGeo; + registry?: EcsRegistry; + url?: EcsUrl; + x509?: EcsX509; +} + /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-threat.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-threat.html * * @internal */ export interface EcsThreat { + enrichments?: Enrichment[]; + indicator?: Indicator; framework?: string; + group?: Group; + software?: Software; tactic?: Tactic; technique?: Technique; } +interface Enrichment { + indicator?: Indicator; + matched?: Matched; +} + +interface Indicator extends IndicatorNestedFields { + confidence?: string; + description?: string; + email?: { address?: string }; + first_seen?: string; + ip?: string; + last_seen?: string; + marking?: { tlp?: string }; + modified_at?: string; + port?: number; + provider?: string; + reference?: string; + scanner_stats?: number; + sightings?: number; + type?: string; +} + +interface Matched { + atomic?: string; + field?: string; + id?: string; + index?: string; + type?: string; +} + +interface Group { + alias?: string[]; + id?: string; + name?: string; + reference?: string; +} + +interface Software { + id?: string; + name?: string; + platforms?: string[]; + reference?: string; + type?: string; +} + interface Tactic { id?: string[]; name?: string[]; diff --git a/packages/kbn-logging/src/ecs/tls.ts b/packages/kbn-logging/src/ecs/tls.ts index b04d03d650908..75667170f6059 100644 --- a/packages/kbn-logging/src/ecs/tls.ts +++ b/packages/kbn-logging/src/ecs/tls.ts @@ -17,7 +17,7 @@ interface NestedServerFields { } /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-tls.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-tls.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/tracing.ts b/packages/kbn-logging/src/ecs/tracing.ts index 1abbbd4b4c8a2..ea1ff300e4c3f 100644 --- a/packages/kbn-logging/src/ecs/tracing.ts +++ b/packages/kbn-logging/src/ecs/tracing.ts @@ -12,7 +12,7 @@ * the base fields, we will need to do an intersection with these types at * the root level. * - * https://www.elastic.co/guide/en/ecs/1.9/ecs-tracing.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-tracing.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/url.ts b/packages/kbn-logging/src/ecs/url.ts index 5985b28a4f6c3..069ff6b09aec5 100644 --- a/packages/kbn-logging/src/ecs/url.ts +++ b/packages/kbn-logging/src/ecs/url.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-url.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-url.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/user.ts b/packages/kbn-logging/src/ecs/user.ts index 3ab0c946b49b7..6bce3552e23ec 100644 --- a/packages/kbn-logging/src/ecs/user.ts +++ b/packages/kbn-logging/src/ecs/user.ts @@ -20,7 +20,7 @@ interface NestedFields { * placed at the root level, but not if it is nested inside another field like * `destination`. A more detailed explanation of these nuances can be found at: * - * https://www.elastic.co/guide/en/ecs/1.9/ecs-user-usage.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-user-usage.html * * As a result, we need to export a separate `NestedUser` type to import into * other interfaces internally. This contains the reusable subset of properties diff --git a/packages/kbn-logging/src/ecs/user_agent.ts b/packages/kbn-logging/src/ecs/user_agent.ts index f77b3ba9e1f0f..0ff8aa681d964 100644 --- a/packages/kbn-logging/src/ecs/user_agent.ts +++ b/packages/kbn-logging/src/ecs/user_agent.ts @@ -13,7 +13,7 @@ interface NestedFields { } /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-user_agent.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-user_agent.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/vlan.ts b/packages/kbn-logging/src/ecs/vlan.ts index 646f8ee17fd03..a2939380ea57f 100644 --- a/packages/kbn-logging/src/ecs/vlan.ts +++ b/packages/kbn-logging/src/ecs/vlan.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-vlan.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-vlan.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/vulnerability.ts b/packages/kbn-logging/src/ecs/vulnerability.ts index 2c26d557d2ba9..a3648a9c5df17 100644 --- a/packages/kbn-logging/src/ecs/vulnerability.ts +++ b/packages/kbn-logging/src/ecs/vulnerability.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-vulnerability.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-vulnerability.html * * @internal */ diff --git a/packages/kbn-logging/src/ecs/x509.ts b/packages/kbn-logging/src/ecs/x509.ts index 35bc1b458579a..a38fb93585c83 100644 --- a/packages/kbn-logging/src/ecs/x509.ts +++ b/packages/kbn-logging/src/ecs/x509.ts @@ -7,7 +7,7 @@ */ /** - * https://www.elastic.co/guide/en/ecs/1.9/ecs-x509.html + * https://www.elastic.co/guide/en/ecs/1.12/ecs-x509.html * * @internal */ diff --git a/src/core/server/logging/__snapshots__/logging_system.test.ts.snap b/src/core/server/logging/__snapshots__/logging_system.test.ts.snap index d74317203d78e..851cfd6cf3bcc 100644 --- a/src/core/server/logging/__snapshots__/logging_system.test.ts.snap +++ b/src/core/server/logging/__snapshots__/logging_system.test.ts.snap @@ -16,7 +16,7 @@ exports[`asLoggerFactory() only allows to create new loggers. 1`] = ` Object { "@timestamp": "2012-01-30T22:33:22.011-05:00", "ecs": Object { - "version": "1.9.0", + "version": "1.12.0", }, "log": Object { "level": "TRACE", @@ -33,7 +33,7 @@ exports[`asLoggerFactory() only allows to create new loggers. 2`] = ` Object { "@timestamp": "2012-01-30T17:33:22.011-05:00", "ecs": Object { - "version": "1.9.0", + "version": "1.12.0", }, "log": Object { "level": "INFO", @@ -51,7 +51,7 @@ exports[`asLoggerFactory() only allows to create new loggers. 3`] = ` Object { "@timestamp": "2012-01-30T12:33:22.011-05:00", "ecs": Object { - "version": "1.9.0", + "version": "1.12.0", }, "log": Object { "level": "FATAL", @@ -68,7 +68,7 @@ exports[`flushes memory buffer logger and switches to real logger once config is Object { "@timestamp": "2012-02-01T09:33:22.011-05:00", "ecs": Object { - "version": "1.9.0", + "version": "1.12.0", }, "log": Object { "level": "INFO", @@ -86,7 +86,7 @@ exports[`flushes memory buffer logger and switches to real logger once config is Object { "@timestamp": "2012-01-31T23:33:22.011-05:00", "ecs": Object { - "version": "1.9.0", + "version": "1.12.0", }, "log": Object { "level": "INFO", diff --git a/src/core/server/logging/layouts/__snapshots__/json_layout.test.ts.snap b/src/core/server/logging/layouts/__snapshots__/json_layout.test.ts.snap index a131d5c8a9248..e128651e61b40 100644 --- a/src/core/server/logging/layouts/__snapshots__/json_layout.test.ts.snap +++ b/src/core/server/logging/layouts/__snapshots__/json_layout.test.ts.snap @@ -1,13 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`\`format()\` correctly formats record. 1`] = `"{\\"ecs\\":{\\"version\\":\\"1.9.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-1\\",\\"error\\":{\\"message\\":\\"Some error message\\",\\"type\\":\\"Some error name\\",\\"stack_trace\\":\\"Some error stack\\"},\\"log\\":{\\"level\\":\\"FATAL\\",\\"logger\\":\\"context-1\\"},\\"process\\":{\\"pid\\":5355}}"`; +exports[`\`format()\` correctly formats record. 1`] = `"{\\"ecs\\":{\\"version\\":\\"1.12.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-1\\",\\"error\\":{\\"message\\":\\"Some error message\\",\\"type\\":\\"Some error name\\",\\"stack_trace\\":\\"Some error stack\\"},\\"log\\":{\\"level\\":\\"FATAL\\",\\"logger\\":\\"context-1\\"},\\"process\\":{\\"pid\\":5355}}"`; -exports[`\`format()\` correctly formats record. 2`] = `"{\\"ecs\\":{\\"version\\":\\"1.9.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-2\\",\\"log\\":{\\"level\\":\\"ERROR\\",\\"logger\\":\\"context-2\\"},\\"process\\":{\\"pid\\":5355}}"`; +exports[`\`format()\` correctly formats record. 2`] = `"{\\"ecs\\":{\\"version\\":\\"1.12.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-2\\",\\"log\\":{\\"level\\":\\"ERROR\\",\\"logger\\":\\"context-2\\"},\\"process\\":{\\"pid\\":5355}}"`; -exports[`\`format()\` correctly formats record. 3`] = `"{\\"ecs\\":{\\"version\\":\\"1.9.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-3\\",\\"log\\":{\\"level\\":\\"WARN\\",\\"logger\\":\\"context-3\\"},\\"process\\":{\\"pid\\":5355}}"`; +exports[`\`format()\` correctly formats record. 3`] = `"{\\"ecs\\":{\\"version\\":\\"1.12.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-3\\",\\"log\\":{\\"level\\":\\"WARN\\",\\"logger\\":\\"context-3\\"},\\"process\\":{\\"pid\\":5355}}"`; -exports[`\`format()\` correctly formats record. 4`] = `"{\\"ecs\\":{\\"version\\":\\"1.9.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-4\\",\\"log\\":{\\"level\\":\\"DEBUG\\",\\"logger\\":\\"context-4\\"},\\"process\\":{\\"pid\\":5355}}"`; +exports[`\`format()\` correctly formats record. 4`] = `"{\\"ecs\\":{\\"version\\":\\"1.12.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-4\\",\\"log\\":{\\"level\\":\\"DEBUG\\",\\"logger\\":\\"context-4\\"},\\"process\\":{\\"pid\\":5355}}"`; -exports[`\`format()\` correctly formats record. 5`] = `"{\\"ecs\\":{\\"version\\":\\"1.9.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-5\\",\\"log\\":{\\"level\\":\\"INFO\\",\\"logger\\":\\"context-5\\"},\\"process\\":{\\"pid\\":5355}}"`; +exports[`\`format()\` correctly formats record. 5`] = `"{\\"ecs\\":{\\"version\\":\\"1.12.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-5\\",\\"log\\":{\\"level\\":\\"INFO\\",\\"logger\\":\\"context-5\\"},\\"process\\":{\\"pid\\":5355}}"`; -exports[`\`format()\` correctly formats record. 6`] = `"{\\"ecs\\":{\\"version\\":\\"1.9.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-6\\",\\"log\\":{\\"level\\":\\"TRACE\\",\\"logger\\":\\"context-6\\"},\\"process\\":{\\"pid\\":5355}}"`; +exports[`\`format()\` correctly formats record. 6`] = `"{\\"ecs\\":{\\"version\\":\\"1.12.0\\"},\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"message\\":\\"message-6\\",\\"log\\":{\\"level\\":\\"TRACE\\",\\"logger\\":\\"context-6\\"},\\"process\\":{\\"pid\\":5355}}"`; diff --git a/src/core/server/logging/layouts/json_layout.test.ts b/src/core/server/logging/layouts/json_layout.test.ts index e76e3fb4402bb..034e2abf46461 100644 --- a/src/core/server/logging/layouts/json_layout.test.ts +++ b/src/core/server/logging/layouts/json_layout.test.ts @@ -74,7 +74,7 @@ test('`format()` correctly formats record.', () => { } }); -test('`format()` correctly formats record with meta-data', () => { +test('`format()` correctly formats record with meta-data and correct ECS version', () => { const layout = new JsonLayout(); expect( @@ -94,7 +94,7 @@ test('`format()` correctly formats record with meta-data', () => { }) ) ).toStrictEqual({ - ecs: { version: '1.9.0' }, + ecs: { version: '1.12.0' }, '@timestamp': '2012-02-01T09:30:22.011-05:00', log: { level: 'DEBUG', @@ -136,7 +136,7 @@ test('`format()` correctly formats error record with meta-data', () => { }) ) ).toStrictEqual({ - ecs: { version: '1.9.0' }, + ecs: { version: expect.any(String) }, '@timestamp': '2012-02-01T09:30:22.011-05:00', log: { level: 'DEBUG', @@ -176,7 +176,7 @@ test('format() meta can merge override logs', () => { }) ) ).toStrictEqual({ - ecs: { version: '1.9.0' }, + ecs: { version: expect.any(String) }, '@timestamp': '2012-02-01T09:30:22.011-05:00', message: 'foo', log: { @@ -206,7 +206,7 @@ test('format() meta can not override message', () => { }) ) ).toStrictEqual({ - ecs: { version: '1.9.0' }, + ecs: { version: expect.any(String) }, '@timestamp': '2012-02-01T09:30:22.011-05:00', message: 'foo', log: { @@ -235,7 +235,7 @@ test('format() meta can not override ecs version', () => { }) ) ).toStrictEqual({ - ecs: { version: '1.9.0' }, + ecs: { version: expect.any(String) }, '@timestamp': '2012-02-01T09:30:22.011-05:00', message: 'foo', log: { @@ -267,7 +267,7 @@ test('format() meta can not override logger or level', () => { }) ) ).toStrictEqual({ - ecs: { version: '1.9.0' }, + ecs: { version: expect.any(String) }, '@timestamp': '2012-02-01T09:30:22.011-05:00', message: 'foo', log: { @@ -296,7 +296,7 @@ test('format() meta can not override timestamp', () => { }) ) ).toStrictEqual({ - ecs: { version: '1.9.0' }, + ecs: { version: expect.any(String) }, '@timestamp': '2012-02-01T09:30:22.011-05:00', message: 'foo', log: { diff --git a/src/core/server/logging/layouts/json_layout.ts b/src/core/server/logging/layouts/json_layout.ts index add88cc01b6d2..820ed32045f15 100644 --- a/src/core/server/logging/layouts/json_layout.ts +++ b/src/core/server/logging/layouts/json_layout.ts @@ -43,7 +43,7 @@ export class JsonLayout implements Layout { public format(record: LogRecord): string { const log: Ecs = { - ecs: { version: '1.9.0' }, + ecs: { version: '1.12.0' }, '@timestamp': moment(record.timestamp).format('YYYY-MM-DDTHH:mm:ss.SSSZ'), message: record.message, error: JsonLayout.errorToSerializableObject(record.error), diff --git a/x-pack/plugins/security/server/audit/audit_events.ts b/x-pack/plugins/security/server/audit/audit_events.ts index 611e7bd456da3..a4025b619365f 100644 --- a/x-pack/plugins/security/server/audit/audit_events.ts +++ b/x-pack/plugins/security/server/audit/audit_events.ts @@ -10,7 +10,7 @@ import type { EcsEventOutcome, EcsEventType, KibanaRequest, LogMeta } from 'src/ import type { AuthenticationResult } from '../authentication/authentication_result'; /** - * Audit event schema using ECS format: https://www.elastic.co/guide/en/ecs/1.9/index.html + * Audit event schema using ECS format: https://www.elastic.co/guide/en/ecs/1.12/index.html * * If you add additional fields to the schema ensure you update the Kibana Filebeat module: * https://github.com/elastic/beats/tree/master/filebeat/module/kibana From ac0ba881f60c5233c1783b54619ad91ff1a3688c Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 15:58:12 -0400 Subject: [PATCH 45/51] Create standards.mdx (#113313) (#113654) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Stacey Gammon --- dev_docs/contributing/standards.mdx | 74 +++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 dev_docs/contributing/standards.mdx diff --git a/dev_docs/contributing/standards.mdx b/dev_docs/contributing/standards.mdx new file mode 100644 index 0000000000000..3d41de8f229cc --- /dev/null +++ b/dev_docs/contributing/standards.mdx @@ -0,0 +1,74 @@ +--- +id: kibStandards +slug: /kibana-dev-docs/standards +title: Standards and guidelines +summary: Standards and guidelines we expect every Kibana developer to abide by +date: 2021-09-28 +tags: ['contributor', 'dev', 'github', 'getting started', 'onboarding', 'kibana'] +--- + +## Developer principles + +We expect all developers to read and abide by our overarching . + +## Style guide + +Please read and abide by our . The majority of these items are linted against but some are not. + +## RESTful HTTP APIs + +### Terminology + +**REST APIs** +Technically, REST does not specify a protocol, but for readability, we’ll be calling RESTful HTTP APIs as REST APIs for short for the remainder of the section. HTTP APIs that serve HTML, CSS and images are not REST APIs. + +**End user** +Anywhere we refer to “end user” in this section, we are referring to someone who is using the REST APIs. The distinction between Product breaking changes and plugin breaking changes can also be found in this [Make it Minor strawman proposal doc](https://docs.google.com/document/d/12R0w75dSNR-VDQLGl2vxFyEHhzxNT38iamYhven9uvw/edit). This can be a tricky distinction, as some folks may consider end user to only be folks that use the Kibana UI. + +### Privacy + +| Type | Description | Guarantees | +| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | +| Internal | An API with “internal” in the route. Specifically it should be `/internal/{pluginname}/{...}`. It should only be used by the plugin that created it. | None | +| Public | Any API that is not internal based on above definition | Based on | + +### Do not access directly from plugins + +Plugins should not attempt to directly access the REST APIs created by another plugin. The plugin author who registered the public REST API should provide access to it via functionality on the plugin lifecycle contract. Accessing functionality through a client side plugin contract provides more type-safety compared to calling the REST API directly. Never attempt to call a server-side REST API if you are already on the server-side. It will not work. This should also be avoided in any code provided within a common folder. + +### Path names + +All public API path names should start with `/api/`. +All internal APIs should start with `/internal/{pluginname}/{...}`. + +### Backward compatibility and breaking changes + +Every public API should have a release tag specified at the top of it’s documentation page. Release tags are not applicable to internal APIs, as we make no guarantees on those. + +#### Release tags + +| Type | Description | Documentation | Asciidoc Tag | +| Undocumented | Every public API should be documented, but if it isn’t, we make no guarantees about it. These need to be eliminated and should become internal or documented. | +| Experimental | A public API that may break or be removed at any time. | experimental[] | +| Beta | A public API that we make a best effort not to break or remove. However, there are no guarantees. | beta[] | +| Stable | No breaking changes outside of a Major\* | stable[] | +| Deprecated | Do not use, will be removed. | deprecated[] | + +\*This is likely to change with Make it Minor as we move towards a calendar based rolling deprecation and removal policy. + +#### What constitutes a breaking change? + +- A path change +- A request payload change that adds a new required variable, or changes an existing one (new optional parameters are not breaking). +- A response payload change that removes data or changes existing data (returning additional data is not breaking). +- Status code changes + +### Telemetry + +Every team should be collecting telemetry metrics on it’s public API usage. This will be important for knowing when it’s safe to make breaking changes. The Core team will be looking into ways to make this easier and an automatic part of registration (see [#112291](https://github.com/elastic/kibana/issues/112291)). + +### Documentation + +Every public API should be documented inside the [docs/api](https://github.com/elastic/kibana/tree/master/docs/api) folder in asciidoc (this content will eventually be migrated to mdx to support the new docs system). If a public REST API is undocumented, you should either document it, or make it internal. + +Every public API should have a release tag specified using the appropriate documentation release tag above. If you do this, the docs system will provide a pop up explaining the conditions. If an API is not marked, it should be considered experimental. From c385d498874d4ca34f454e3cb9ad9e4c0e3219ae Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Fri, 1 Oct 2021 15:17:16 -0700 Subject: [PATCH 46/51] Revert "[Upgrade Assistant] Refactor telemetry (#112177)" (#113665) This reverts commit 991d24bad21ccf4b8350cba2b2ed3ceca6d90cea. --- .../schema/xpack_plugins.json | 38 ++++ x-pack/plugins/upgrade_assistant/README.md | 27 +-- .../plugins/upgrade_assistant/common/types.ts | 29 +++ .../index_settings/flyout.tsx | 16 +- .../deprecation_types/ml_snapshots/flyout.tsx | 8 - .../checklist_step.test.tsx.snap | 4 +- .../reindex/flyout/checklist_step.tsx | 22 +-- .../deprecation_types/reindex/table_row.tsx | 21 +- .../reindex/use_reindex_state.tsx | 4 + .../es_deprecations/es_deprecations.tsx | 22 ++- .../deprecation_details_flyout.tsx | 11 +- .../kibana_deprecations.tsx | 13 +- .../overview/backup_step/cloud_backup.tsx | 8 +- .../overview/backup_step/on_prem_backup.tsx | 11 +- .../deprecations_count_checkpoint.tsx | 5 +- .../overview/fix_logs_step/external_links.tsx | 27 +-- .../components/overview/overview.tsx | 13 +- .../public/application/lib/api.ts | 20 ++ .../public/application/lib/ui_metric.ts | 49 ----- .../upgrade_assistant/public/plugin.ts | 10 +- .../plugins/upgrade_assistant/public/types.ts | 2 - .../lib/telemetry/es_ui_open_apis.test.ts | 48 +++++ .../server/lib/telemetry/es_ui_open_apis.ts | 57 ++++++ .../lib/telemetry/es_ui_reindex_apis.test.ts | 52 +++++ .../lib/telemetry/es_ui_reindex_apis.ts | 63 ++++++ .../lib/telemetry/usage_collector.test.ts | 31 +++ .../server/lib/telemetry/usage_collector.ts | 113 ++++++++++- .../upgrade_assistant/server/plugin.ts | 3 +- .../server/routes/register_routes.ts | 2 + .../server/routes/telemetry.test.ts | 187 ++++++++++++++++++ .../server/routes/telemetry.ts | 64 ++++++ .../telemetry_saved_object_type.ts | 36 ++++ 32 files changed, 809 insertions(+), 207 deletions(-) delete mode 100644 x-pack/plugins/upgrade_assistant/public/application/lib/ui_metric.ts create mode 100644 x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.test.ts create mode 100644 x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.ts create mode 100644 x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_reindex_apis.test.ts create mode 100644 x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_reindex_apis.ts create mode 100644 x-pack/plugins/upgrade_assistant/server/routes/telemetry.test.ts create mode 100644 x-pack/plugins/upgrade_assistant/server/routes/telemetry.ts diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index 2dd7b96cfef95..a46cc00317463 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -7324,6 +7324,44 @@ } } } + }, + "ui_open": { + "properties": { + "elasticsearch": { + "type": "long", + "_meta": { + "description": "Number of times a user viewed the list of Elasticsearch deprecations." + } + }, + "overview": { + "type": "long", + "_meta": { + "description": "Number of times a user viewed the Overview page." + } + }, + "kibana": { + "type": "long", + "_meta": { + "description": "Number of times a user viewed the list of Kibana deprecations" + } + } + } + }, + "ui_reindex": { + "properties": { + "close": { + "type": "long" + }, + "open": { + "type": "long" + }, + "start": { + "type": "long" + }, + "stop": { + "type": "long" + } + } } } }, diff --git a/x-pack/plugins/upgrade_assistant/README.md b/x-pack/plugins/upgrade_assistant/README.md index 6570e7f8d7617..255eb94a0318c 100644 --- a/x-pack/plugins/upgrade_assistant/README.md +++ b/x-pack/plugins/upgrade_assistant/README.md @@ -226,29 +226,4 @@ This is a non-exhaustive list of different error scenarios in Upgrade Assistant. - **Error updating deprecation logging status.** Mock a `404` status code to `PUT /api/upgrade_assistant/deprecation_logging`. Alternatively, edit [this line](https://github.com/elastic/kibana/blob/545c1420c285af8f5eee56f414bd6eca735aea11/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts#L77) locally and replace `deprecation_logging` with `fake_deprecation_logging`. - **Unauthorized error fetching ES deprecations.** Mock a `403` status code to `GET /api/upgrade_assistant/es_deprecations` with the response payload: `{ "statusCode": 403 }` - **Partially upgraded error fetching ES deprecations.** Mock a `426` status code to `GET /api/upgrade_assistant/es_deprecations` with the response payload: `{ "statusCode": 426, "attributes": { "allNodesUpgraded": false } }` -- **Upgraded error fetching ES deprecations.** Mock a `426` status code to `GET /api/upgrade_assistant/es_deprecations` with the response payload: `{ "statusCode": 426, "attributes": { "allNodesUpgraded": true } }` - -### Telemetry - -The Upgrade Assistant tracks several triggered events in the UI, using Kibana Usage Collection service's [UI counters](https://github.com/elastic/kibana/blob/master/src/plugins/usage_collection/README.mdx#ui-counters). - -**Overview page** -- Component loaded -- Click event for "Create snapshot" button -- Click event for "View deprecation logs in Observability" link -- Click event for "Analyze logs in Discover" link -- Click event for "Reset counter" button - -**ES deprecations page** -- Component loaded -- Click events for starting and stopping reindex tasks -- Click events for upgrading or deleting a Machine Learning snapshot -- Click event for deleting a deprecated index setting - -**Kibana deprecations page** -- Component loaded -- Click event for "Quick resolve" button - -In addition to UI counters, the Upgrade Assistant has a [custom usage collector](https://github.com/elastic/kibana/blob/master/src/plugins/usage_collection/README.mdx#custom-collector). It currently is only responsible for tracking whether the user has deprecation logging enabled or not. - -For testing instructions, refer to the [Kibana Usage Collection service README](https://github.com/elastic/kibana/blob/master/src/plugins/usage_collection/README.mdx#testing). \ No newline at end of file +- **Upgraded error fetching ES deprecations.** Mock a `426` status code to `GET /api/upgrade_assistant/es_deprecations` with the response payload: `{ "statusCode": 426, "attributes": { "allNodesUpgraded": true } }` \ No newline at end of file diff --git a/x-pack/plugins/upgrade_assistant/common/types.ts b/x-pack/plugins/upgrade_assistant/common/types.ts index b8b05efacb590..928c06f0aa5d4 100644 --- a/x-pack/plugins/upgrade_assistant/common/types.ts +++ b/x-pack/plugins/upgrade_assistant/common/types.ts @@ -141,7 +141,32 @@ export interface UIReindex { stop: boolean; } +export interface UpgradeAssistantTelemetrySavedObject { + ui_open: { + overview: number; + elasticsearch: number; + kibana: number; + }; + ui_reindex: { + close: number; + open: number; + start: number; + stop: number; + }; +} + export interface UpgradeAssistantTelemetry { + ui_open: { + overview: number; + elasticsearch: number; + kibana: number; + }; + ui_reindex: { + close: number; + open: number; + start: number; + stop: number; + }; features: { deprecation_logging: { enabled: boolean; @@ -149,6 +174,10 @@ export interface UpgradeAssistantTelemetry { }; } +export interface UpgradeAssistantTelemetrySavedObjectAttributes { + [key: string]: any; +} + export type MIGRATION_DEPRECATION_LEVEL = 'none' | 'info' | 'warning' | 'critical'; export interface DeprecationInfo { level: MIGRATION_DEPRECATION_LEVEL; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.tsx index e00edb4f3b11d..24c1897fbdd02 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.tsx @@ -5,9 +5,8 @@ * 2.0. */ -import React, { useCallback } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; -import { METRIC_TYPE } from '@kbn/analytics'; import { EuiButton, EuiButtonEmpty, @@ -26,7 +25,6 @@ import { } from '@elastic/eui'; import { EnrichedDeprecationInfo, IndexSettingAction } from '../../../../../../common/types'; import type { ResponseError } from '../../../../lib/api'; -import { uiMetricService, UIM_INDEX_SETTINGS_DELETE_CLICK } from '../../../../lib/ui_metric'; import type { Status } from '../../../types'; import { DeprecationBadge } from '../../../shared'; @@ -109,11 +107,6 @@ export const RemoveIndexSettingsFlyout = ({ // Flag used to hide certain parts of the UI if the deprecation has been resolved or is in progress const isResolvable = ['idle', 'error'].includes(statusType); - const onRemoveSettings = useCallback(() => { - uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_INDEX_SETTINGS_DELETE_CLICK); - removeIndexSettings(index!, (correctiveAction as IndexSettingAction).deprecatedSettings); - }, [correctiveAction, index, removeIndexSettings]); - return ( <> @@ -197,7 +190,12 @@ export const RemoveIndexSettingsFlyout = ({ fill data-test-subj="deleteSettingsButton" color="danger" - onClick={onRemoveSettings} + onClick={() => + removeIndexSettings( + index!, + (correctiveAction as IndexSettingAction).deprecatedSettings + ) + } > {statusType === 'error' ? i18nTexts.retryRemoveButtonLabel diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.tsx index 2a36f3e33e83c..4e3d77ba72ae8 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.tsx @@ -8,7 +8,6 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { METRIC_TYPE } from '@kbn/analytics'; import { EuiButton, @@ -26,11 +25,6 @@ import { } from '@elastic/eui'; import { EnrichedDeprecationInfo } from '../../../../../../common/types'; -import { - uiMetricService, - UIM_ML_SNAPSHOT_UPGRADE_CLICK, - UIM_ML_SNAPSHOT_DELETE_CLICK, -} from '../../../../lib/ui_metric'; import { DeprecationBadge } from '../../../shared'; import { MlSnapshotContext } from './context'; import { useAppContext } from '../../../../app_context'; @@ -179,13 +173,11 @@ export const FixSnapshotsFlyout = ({ const isResolved = snapshotState.status === 'complete'; const onUpgradeSnapshot = () => { - uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_ML_SNAPSHOT_UPGRADE_CLICK); upgradeSnapshot(); closeFlyout(); }; const onDeleteSnapshot = () => { - uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_ML_SNAPSHOT_DELETE_CLICK); deleteSnapshot(); closeFlyout(); }; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/__snapshots__/checklist_step.test.tsx.snap b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/__snapshots__/checklist_step.test.tsx.snap index 542cc51e45b9c..26119c2b62040 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/__snapshots__/checklist_step.test.tsx.snap +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/__snapshots__/checklist_step.test.tsx.snap @@ -42,7 +42,7 @@ exports[`ChecklistFlyout renders 1`] = `

{ - uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_START_CLICK); - startReindex(); - }, [startReindex]); - - const onStopReindex = useCallback(() => { - uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_STOP_CLICK); - cancelReindex(); - }, [cancelReindex]); - return ( @@ -168,7 +152,7 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{ /> - + @@ -186,7 +170,7 @@ export const ChecklistFlyoutStep: React.FunctionComponent<{ fill color={status === ReindexStatus.paused ? 'warning' : 'primary'} iconType={status === ReindexStatus.paused ? 'play' : undefined} - onClick={onStartReindex} + onClick={startReindex} isLoading={loading} disabled={loading || !hasRequiredPrivileges} data-test-subj="startReindexingButton" diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/table_row.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/table_row.tsx index 1059720e66a59..c2a14ca5be858 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/table_row.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/table_row.tsx @@ -7,15 +7,9 @@ import React, { useState, useEffect, useCallback } from 'react'; import { EuiTableRowCell } from '@elastic/eui'; -import { METRIC_TYPE } from '@kbn/analytics'; import { EnrichedDeprecationInfo } from '../../../../../../common/types'; import { GlobalFlyout } from '../../../../../shared_imports'; import { useAppContext } from '../../../../app_context'; -import { - uiMetricService, - UIM_REINDEX_CLOSE_FLYOUT_CLICK, - UIM_REINDEX_OPEN_FLYOUT_CLICK, -} from '../../../../lib/ui_metric'; import { DeprecationTableColumns } from '../../../types'; import { EsDeprecationsTableCells } from '../../es_deprecations_table_cells'; import { ReindexResolutionCell } from './resolution_table_cell'; @@ -35,6 +29,9 @@ const ReindexTableRowCells: React.FunctionComponent = ({ }) => { const [showFlyout, setShowFlyout] = useState(false); const reindexState = useReindexContext(); + const { + services: { api }, + } = useAppContext(); const { addContent: addContentToGlobalFlyout, removeContent: removeContentFromGlobalFlyout } = useGlobalFlyout(); @@ -42,8 +39,8 @@ const ReindexTableRowCells: React.FunctionComponent = ({ const closeFlyout = useCallback(async () => { removeContentFromGlobalFlyout('reindexFlyout'); setShowFlyout(false); - uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_CLOSE_FLYOUT_CLICK); - }, [removeContentFromGlobalFlyout]); + await api.sendReindexTelemetryData({ close: true }); + }, [api, removeContentFromGlobalFlyout]); useEffect(() => { if (showFlyout) { @@ -67,9 +64,13 @@ const ReindexTableRowCells: React.FunctionComponent = ({ useEffect(() => { if (showFlyout) { - uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_REINDEX_OPEN_FLYOUT_CLICK); + async function sendTelemetry() { + await api.sendReindexTelemetryData({ open: true }); + } + + sendTelemetry(); } - }, [showFlyout]); + }, [showFlyout, api]); return ( <> diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/use_reindex_state.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/use_reindex_state.tsx index 8890411cca2a0..87891c5d8d219 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/use_reindex_state.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/use_reindex_state.tsx @@ -132,6 +132,8 @@ export const useReindexStatus = ({ indexName, api }: { indexName: string; api: A cancelLoadingState: undefined, }); + api.sendReindexTelemetryData({ start: true }); + const { data, error } = await api.startReindexTask(indexName); if (error) { @@ -149,6 +151,8 @@ export const useReindexStatus = ({ indexName, api }: { indexName: string; api: A }, [api, indexName, reindexState, updateStatus]); const cancelReindex = useCallback(async () => { + api.sendReindexTelemetryData({ stop: true }); + const { error } = await api.cancelReindexTask(indexName); setReindexState({ diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.tsx index 7c3394d5a9c0f..c7d157c342c77 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.tsx @@ -12,12 +12,10 @@ import { EuiPageHeader, EuiSpacer, EuiPageContent, EuiLink } from '@elastic/eui' import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { DocLinksStart } from 'kibana/public'; -import { METRIC_TYPE } from '@kbn/analytics'; import { EnrichedDeprecationInfo } from '../../../../common/types'; import { SectionLoading } from '../../../shared_imports'; import { useAppContext } from '../../app_context'; -import { uiMetricService, UIM_ES_DEPRECATIONS_PAGE_LOAD } from '../../lib/ui_metric'; import { getEsDeprecationError } from '../../lib/get_es_deprecation_error'; import { DeprecationsPageLoadingError, NoDeprecationsPrompt, DeprecationCount } from '../shared'; import { EsDeprecationsTable } from './es_deprecations_table'; @@ -84,7 +82,13 @@ export const EsDeprecations = withRouter(({ history }: RouteComponentProps) => { }, } = useAppContext(); - const { data: esDeprecations, isLoading, error, resendRequest } = api.useLoadEsDeprecations(); + const { + data: esDeprecations, + isLoading, + error, + resendRequest, + isInitialRequest, + } = api.useLoadEsDeprecations(); const deprecationsCountByLevel: { warningDeprecations: number; @@ -99,8 +103,16 @@ export const EsDeprecations = withRouter(({ history }: RouteComponentProps) => { }, [breadcrumbs]); useEffect(() => { - uiMetricService.trackUiMetric(METRIC_TYPE.LOADED, UIM_ES_DEPRECATIONS_PAGE_LOAD); - }, []); + if (isLoading === false && isInitialRequest) { + async function sendTelemetryData() { + await api.sendPageTelemetryData({ + elasticsearch: true, + }); + } + + sendTelemetryData(); + } + }, [api, isLoading, isInitialRequest]); if (error) { return ( diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/deprecation_details_flyout.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/deprecation_details_flyout.tsx index 37b07bc159577..a1242c2da9b0c 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/deprecation_details_flyout.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/deprecation_details_flyout.tsx @@ -5,10 +5,9 @@ * 2.0. */ -import React, { useCallback } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { METRIC_TYPE } from '@kbn/analytics'; import { EuiButtonEmpty, @@ -25,7 +24,6 @@ import { EuiSpacer, } from '@elastic/eui'; -import { uiMetricService, UIM_KIBANA_QUICK_RESOLVE_CLICK } from '../../lib/ui_metric'; import type { DeprecationResolutionState, KibanaDeprecationDetails } from './kibana_deprecations'; import { DeprecationBadge } from '../shared'; @@ -136,11 +134,6 @@ export const DeprecationDetailsFlyout = ({ const isCurrent = deprecationResolutionState?.id === deprecation.id; const isResolved = isCurrent && deprecationResolutionState?.resolveDeprecationStatus === 'ok'; - const onResolveDeprecation = useCallback(() => { - uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_KIBANA_QUICK_RESOLVE_CLICK); - resolveDeprecation(deprecation); - }, [deprecation, resolveDeprecation]); - return ( <> @@ -242,7 +235,7 @@ export const DeprecationDetailsFlyout = ({ resolveDeprecation(deprecation)} isLoading={Boolean( deprecationResolutionState?.resolveDeprecationStatus === 'in_progress' )} diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx index b488c84f255cc..23697b00923c8 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx @@ -11,12 +11,10 @@ import { withRouter, RouteComponentProps } from 'react-router-dom'; import { EuiPageContent, EuiPageHeader, EuiSpacer, EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { METRIC_TYPE } from '@kbn/analytics'; import type { DomainDeprecationDetails } from 'kibana/public'; import { SectionLoading, GlobalFlyout } from '../../../shared_imports'; import { useAppContext } from '../../app_context'; -import { uiMetricService, UIM_KIBANA_DEPRECATIONS_PAGE_LOAD } from '../../lib/ui_metric'; import { DeprecationsPageLoadingError, NoDeprecationsPrompt, DeprecationCount } from '../shared'; import { KibanaDeprecationsTable } from './kibana_deprecations_table'; import { @@ -118,6 +116,7 @@ export const KibanaDeprecations = withRouter(({ history }: RouteComponentProps) services: { core: { deprecations }, breadcrumbs, + api, }, } = useAppContext(); @@ -226,8 +225,14 @@ export const KibanaDeprecations = withRouter(({ history }: RouteComponentProps) ]); useEffect(() => { - uiMetricService.trackUiMetric(METRIC_TYPE.LOADED, UIM_KIBANA_DEPRECATIONS_PAGE_LOAD); - }, []); + async function sendTelemetryData() { + await api.sendPageTelemetryData({ + kibana: true, + }); + } + + sendTelemetryData(); + }, [api]); useEffect(() => { breadcrumbs.setBreadcrumbs('kibanaDeprecations'); diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/backup_step/cloud_backup.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/backup_step/cloud_backup.tsx index 4ab860a0bf6a7..55a6ee8e5c73f 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/backup_step/cloud_backup.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/backup_step/cloud_backup.tsx @@ -9,7 +9,6 @@ import React, { useEffect } from 'react'; import moment from 'moment-timezone'; import { FormattedDate, FormattedTime, FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { METRIC_TYPE } from '@kbn/analytics'; import { EuiLoadingContent, EuiFlexGroup, @@ -22,7 +21,6 @@ import { } from '@elastic/eui'; import { useAppContext } from '../../../app_context'; -import { uiMetricService, UIM_BACKUP_DATA_CLOUD_CLICK } from '../../../lib/ui_metric'; interface Props { cloudSnapshotsUrl: string; @@ -130,13 +128,11 @@ export const CloudBackup: React.FunctionComponent = ({ return ( <> {statusMessage} + - {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} + { - uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_BACKUP_DATA_CLOUD_CLICK); - }} data-test-subj="cloudSnapshotsLink" target="_blank" iconType="popout" diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/backup_step/on_prem_backup.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/backup_step/on_prem_backup.tsx index 69100b36db7eb..2e2e2bd5ce48e 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/backup_step/on_prem_backup.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/backup_step/on_prem_backup.tsx @@ -8,11 +8,9 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { METRIC_TYPE } from '@kbn/analytics'; import { EuiText, EuiButton, EuiSpacer } from '@elastic/eui'; import { useAppContext } from '../../../app_context'; -import { uiMetricService, UIM_BACKUP_DATA_ON_PREM_CLICK } from '../../../lib/ui_metric'; const SnapshotRestoreAppLink: React.FunctionComponent = () => { const { @@ -24,14 +22,7 @@ const SnapshotRestoreAppLink: React.FunctionComponent = () => { ?.useUrl({ page: 'snapshots' }); return ( - // eslint-disable-next-line @elastic/eui/href-or-on-click - { - uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_BACKUP_DATA_ON_PREM_CLICK); - }} - data-test-subj="snapshotRestoreLink" - > + ( @@ -73,7 +71,6 @@ export const DeprecationsCountCheckpoint: FunctionComponent = ({ const onResetClick = () => { const now = moment().toISOString(); - uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_RESET_LOGS_COUNTER_CLICK); setCheckpoint(now); }; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/external_links.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/external_links.tsx index 69f1f14d4eb58..d027b2f262e9e 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/external_links.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/external_links.tsx @@ -9,17 +9,10 @@ import { encode } from 'rison-node'; import React, { FunctionComponent, useState, useEffect } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { METRIC_TYPE } from '@kbn/analytics'; import { EuiLink, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiPanel, EuiText } from '@elastic/eui'; -import { DataPublicPluginStart } from '../../../../shared_imports'; import { useAppContext } from '../../../app_context'; -import { - uiMetricService, - UIM_OBSERVABILITY_CLICK, - UIM_DISCOVER_CLICK, -} from '../../../lib/ui_metric'; - +import { DataPublicPluginStart } from '../../../../shared_imports'; import { DEPRECATION_LOGS_INDEX_PATTERN, DEPRECATION_LOGS_SOURCE_ID, @@ -80,14 +73,7 @@ const DiscoverAppLink: FunctionComponent = ({ checkpoint }) => { }, [dataService, checkpoint, share.url.locators]); return ( - // eslint-disable-next-line @elastic/eui/href-or-on-click - { - uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_DISCOVER_CLICK); - }} - data-test-subj="viewDiscoverLogs" - > + = ({ checkpoint }) => { ); return ( - // eslint-disable-next-line @elastic/eui/href-or-on-click - { - uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_OBSERVABILITY_CLICK); - }} - data-test-subj="viewObserveLogs" - > + { kibanaVersionInfo: { nextMajor }, services: { breadcrumbs, + api, core: { docLinks }, }, plugins: { cloud }, } = useAppContext(); useEffect(() => { - uiMetricService.trackUiMetric(METRIC_TYPE.LOADED, UIM_OVERVIEW_PAGE_LOAD); - }, []); + async function sendTelemetryData() { + await api.sendPageTelemetryData({ + overview: true, + }); + } + + sendTelemetryData(); + }, [api]); useEffect(() => { breadcrumbs.setBreadcrumbs('overview'); diff --git a/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts b/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts index 1d51510333ef4..da4f87f497467 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts @@ -65,6 +65,16 @@ export class ApiService { }); } + public async sendPageTelemetryData(telemetryData: { [tabName: string]: boolean }) { + const result = await this.sendRequest({ + path: `${API_BASE_PATH}/stats/ui_open`, + method: 'put', + body: JSON.stringify(telemetryData), + }); + + return result; + } + public useLoadDeprecationLogging() { return this.useRequest<{ isDeprecationLogIndexingEnabled: boolean; @@ -140,6 +150,16 @@ export class ApiService { }); } + public async sendReindexTelemetryData(telemetryData: { [key: string]: boolean }) { + const result = await this.sendRequest({ + path: `${API_BASE_PATH}/stats/ui_reindex`, + method: 'put', + body: JSON.stringify(telemetryData), + }); + + return result; + } + public async getReindexStatus(indexName: string) { return await this.sendRequest({ path: `${API_BASE_PATH}/reindex/${indexName}`, diff --git a/x-pack/plugins/upgrade_assistant/public/application/lib/ui_metric.ts b/x-pack/plugins/upgrade_assistant/public/application/lib/ui_metric.ts deleted file mode 100644 index 394f046a8bafe..0000000000000 --- a/x-pack/plugins/upgrade_assistant/public/application/lib/ui_metric.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { UiCounterMetricType } from '@kbn/analytics'; -import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; - -export const UIM_APP_NAME = 'upgrade_assistant'; -export const UIM_ES_DEPRECATIONS_PAGE_LOAD = 'es_deprecations_page_load'; -export const UIM_KIBANA_DEPRECATIONS_PAGE_LOAD = 'kibana_deprecations_page_load'; -export const UIM_OVERVIEW_PAGE_LOAD = 'overview_page_load'; -export const UIM_REINDEX_OPEN_FLYOUT_CLICK = 'reindex_open_flyout_click'; -export const UIM_REINDEX_CLOSE_FLYOUT_CLICK = 'reindex_close_flyout_click'; -export const UIM_REINDEX_START_CLICK = 'reindex_start_click'; -export const UIM_REINDEX_STOP_CLICK = 'reindex_stop_click'; -export const UIM_BACKUP_DATA_CLOUD_CLICK = 'backup_data_cloud_click'; -export const UIM_BACKUP_DATA_ON_PREM_CLICK = 'backup_data_on_prem_click'; -export const UIM_RESET_LOGS_COUNTER_CLICK = 'reset_logs_counter_click'; -export const UIM_OBSERVABILITY_CLICK = 'observability_click'; -export const UIM_DISCOVER_CLICK = 'discover_click'; -export const UIM_ML_SNAPSHOT_UPGRADE_CLICK = 'ml_snapshot_upgrade_click'; -export const UIM_ML_SNAPSHOT_DELETE_CLICK = 'ml_snapshot_delete_click'; -export const UIM_INDEX_SETTINGS_DELETE_CLICK = 'index_settings_delete_click'; -export const UIM_KIBANA_QUICK_RESOLVE_CLICK = 'kibana_quick_resolve_click'; - -export class UiMetricService { - private usageCollection: UsageCollectionSetup | undefined; - - public setup(usageCollection: UsageCollectionSetup) { - this.usageCollection = usageCollection; - } - - private track(metricType: UiCounterMetricType, eventName: string | string[]) { - if (!this.usageCollection) { - // Usage collection might be disabled in Kibana config. - return; - } - return this.usageCollection.reportUiCounter(UIM_APP_NAME, metricType, eventName); - } - - public trackUiMetric(metricType: UiCounterMetricType, eventName: string | string[]) { - return this.track(metricType, eventName); - } -} - -export const uiMetricService = new UiMetricService(); diff --git a/x-pack/plugins/upgrade_assistant/public/plugin.ts b/x-pack/plugins/upgrade_assistant/public/plugin.ts index d688ee510ce1f..64c21997b12c4 100644 --- a/x-pack/plugins/upgrade_assistant/public/plugin.ts +++ b/x-pack/plugins/upgrade_assistant/public/plugin.ts @@ -11,7 +11,6 @@ import { Plugin, CoreSetup, PluginInitializerContext } from 'src/core/public'; import { apiService } from './application/lib/api'; import { breadcrumbService } from './application/lib/breadcrumbs'; -import { uiMetricService } from './application/lib/ui_metric'; import { SetupDependencies, StartDependencies, AppDependencies } from './types'; import { Config } from '../common/config'; @@ -19,10 +18,7 @@ export class UpgradeAssistantUIPlugin implements Plugin { constructor(private ctx: PluginInitializerContext) {} - setup( - coreSetup: CoreSetup, - { management, cloud, share, usageCollection }: SetupDependencies - ) { + setup(coreSetup: CoreSetup, { management, cloud, share }: SetupDependencies) { const { readonly } = this.ctx.config.get(); const appRegistrar = management.sections.section.stack; @@ -38,10 +34,6 @@ export class UpgradeAssistantUIPlugin defaultMessage: 'Upgrade Assistant', }); - if (usageCollection) { - uiMetricService.setup(usageCollection); - } - appRegistrar.registerApp({ id: 'upgrade_assistant', title: pluginName, diff --git a/x-pack/plugins/upgrade_assistant/public/types.ts b/x-pack/plugins/upgrade_assistant/public/types.ts index e58c90336d856..de5f29593b7c6 100644 --- a/x-pack/plugins/upgrade_assistant/public/types.ts +++ b/x-pack/plugins/upgrade_assistant/public/types.ts @@ -10,7 +10,6 @@ import { ManagementSetup } from 'src/plugins/management/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; import { SharePluginSetup } from 'src/plugins/share/public'; import { CoreStart } from 'src/core/public'; -import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; import { CloudSetup } from '../../cloud/public'; import { LicensingPluginStart } from '../../licensing/public'; import { BreadcrumbService } from './application/lib/breadcrumbs'; @@ -26,7 +25,6 @@ export interface SetupDependencies { management: ManagementSetup; share: SharePluginSetup; cloud?: CloudSetup; - usageCollection?: UsageCollectionSetup; } export interface StartDependencies { diff --git a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.test.ts new file mode 100644 index 0000000000000..caff78390b9d1 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.test.ts @@ -0,0 +1,48 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { savedObjectsRepositoryMock } from 'src/core/server/mocks'; +import { UPGRADE_ASSISTANT_DOC_ID, UPGRADE_ASSISTANT_TYPE } from '../../../common/types'; + +import { upsertUIOpenOption } from './es_ui_open_apis'; + +/** + * Since these route callbacks are so thin, these serve simply as integration tests + * to ensure they're wired up to the lib functions correctly. Business logic is tested + * more thoroughly in the lib/telemetry tests. + */ +describe('Upgrade Assistant Telemetry SavedObject UIOpen', () => { + describe('Upsert UIOpen Option', () => { + it('call saved objects internal repository with the correct info', async () => { + const internalRepo = savedObjectsRepositoryMock.create(); + + await upsertUIOpenOption({ + overview: true, + elasticsearch: true, + kibana: true, + savedObjects: { createInternalRepository: () => internalRepo } as any, + }); + + expect(internalRepo.incrementCounter).toHaveBeenCalledTimes(3); + expect(internalRepo.incrementCounter).toHaveBeenCalledWith( + UPGRADE_ASSISTANT_TYPE, + UPGRADE_ASSISTANT_DOC_ID, + ['ui_open.overview'] + ); + expect(internalRepo.incrementCounter).toHaveBeenCalledWith( + UPGRADE_ASSISTANT_TYPE, + UPGRADE_ASSISTANT_DOC_ID, + ['ui_open.elasticsearch'] + ); + expect(internalRepo.incrementCounter).toHaveBeenCalledWith( + UPGRADE_ASSISTANT_TYPE, + UPGRADE_ASSISTANT_DOC_ID, + ['ui_open.kibana'] + ); + }); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.ts b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.ts new file mode 100644 index 0000000000000..3d463fe4b03ed --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.ts @@ -0,0 +1,57 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectsServiceStart } from 'src/core/server'; +import { + UIOpen, + UIOpenOption, + UPGRADE_ASSISTANT_DOC_ID, + UPGRADE_ASSISTANT_TYPE, +} from '../../../common/types'; + +interface IncrementUIOpenDependencies { + uiOpenOptionCounter: UIOpenOption; + savedObjects: SavedObjectsServiceStart; +} + +async function incrementUIOpenOptionCounter({ + savedObjects, + uiOpenOptionCounter, +}: IncrementUIOpenDependencies) { + const internalRepository = savedObjects.createInternalRepository(); + + await internalRepository.incrementCounter(UPGRADE_ASSISTANT_TYPE, UPGRADE_ASSISTANT_DOC_ID, [ + `ui_open.${uiOpenOptionCounter}`, + ]); +} + +type UpsertUIOpenOptionDependencies = UIOpen & { savedObjects: SavedObjectsServiceStart }; + +export async function upsertUIOpenOption({ + overview, + elasticsearch, + savedObjects, + kibana, +}: UpsertUIOpenOptionDependencies): Promise { + if (overview) { + await incrementUIOpenOptionCounter({ savedObjects, uiOpenOptionCounter: 'overview' }); + } + + if (elasticsearch) { + await incrementUIOpenOptionCounter({ savedObjects, uiOpenOptionCounter: 'elasticsearch' }); + } + + if (kibana) { + await incrementUIOpenOptionCounter({ savedObjects, uiOpenOptionCounter: 'kibana' }); + } + + return { + overview, + elasticsearch, + kibana, + }; +} diff --git a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_reindex_apis.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_reindex_apis.test.ts new file mode 100644 index 0000000000000..6a05e8a697bb8 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_reindex_apis.test.ts @@ -0,0 +1,52 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { savedObjectsRepositoryMock } from 'src/core/server/mocks'; +import { UPGRADE_ASSISTANT_DOC_ID, UPGRADE_ASSISTANT_TYPE } from '../../../common/types'; +import { upsertUIReindexOption } from './es_ui_reindex_apis'; + +/** + * Since these route callbacks are so thin, these serve simply as integration tests + * to ensure they're wired up to the lib functions correctly. Business logic is tested + * more thoroughly in the lib/telemetry tests. + */ +describe('Upgrade Assistant Telemetry SavedObject UIReindex', () => { + describe('Upsert UIReindex Option', () => { + it('call saved objects internal repository with the correct info', async () => { + const internalRepo = savedObjectsRepositoryMock.create(); + await upsertUIReindexOption({ + close: true, + open: true, + start: true, + stop: true, + savedObjects: { createInternalRepository: () => internalRepo } as any, + }); + + expect(internalRepo.incrementCounter).toHaveBeenCalledTimes(4); + expect(internalRepo.incrementCounter).toHaveBeenCalledWith( + UPGRADE_ASSISTANT_TYPE, + UPGRADE_ASSISTANT_DOC_ID, + [`ui_reindex.close`] + ); + expect(internalRepo.incrementCounter).toHaveBeenCalledWith( + UPGRADE_ASSISTANT_TYPE, + UPGRADE_ASSISTANT_DOC_ID, + [`ui_reindex.open`] + ); + expect(internalRepo.incrementCounter).toHaveBeenCalledWith( + UPGRADE_ASSISTANT_TYPE, + UPGRADE_ASSISTANT_DOC_ID, + [`ui_reindex.start`] + ); + expect(internalRepo.incrementCounter).toHaveBeenCalledWith( + UPGRADE_ASSISTANT_TYPE, + UPGRADE_ASSISTANT_DOC_ID, + [`ui_reindex.stop`] + ); + }); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_reindex_apis.ts b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_reindex_apis.ts new file mode 100644 index 0000000000000..caee1a58a4006 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/es_ui_reindex_apis.ts @@ -0,0 +1,63 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectsServiceStart } from 'src/core/server'; +import { + UIReindex, + UIReindexOption, + UPGRADE_ASSISTANT_DOC_ID, + UPGRADE_ASSISTANT_TYPE, +} from '../../../common/types'; + +interface IncrementUIReindexOptionDependencies { + uiReindexOptionCounter: UIReindexOption; + savedObjects: SavedObjectsServiceStart; +} + +async function incrementUIReindexOptionCounter({ + savedObjects, + uiReindexOptionCounter, +}: IncrementUIReindexOptionDependencies) { + const internalRepository = savedObjects.createInternalRepository(); + + await internalRepository.incrementCounter(UPGRADE_ASSISTANT_TYPE, UPGRADE_ASSISTANT_DOC_ID, [ + `ui_reindex.${uiReindexOptionCounter}`, + ]); +} + +type UpsertUIReindexOptionDepencies = UIReindex & { savedObjects: SavedObjectsServiceStart }; + +export async function upsertUIReindexOption({ + start, + close, + open, + stop, + savedObjects, +}: UpsertUIReindexOptionDepencies): Promise { + if (close) { + await incrementUIReindexOptionCounter({ savedObjects, uiReindexOptionCounter: 'close' }); + } + + if (open) { + await incrementUIReindexOptionCounter({ savedObjects, uiReindexOptionCounter: 'open' }); + } + + if (start) { + await incrementUIReindexOptionCounter({ savedObjects, uiReindexOptionCounter: 'start' }); + } + + if (stop) { + await incrementUIReindexOptionCounter({ savedObjects, uiReindexOptionCounter: 'stop' }); + } + + return { + close, + open, + start, + stop, + }; +} diff --git a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts index 34d329557f11e..50c5b358aa5cb 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts @@ -47,6 +47,26 @@ describe('Upgrade Assistant Usage Collector', () => { }; dependencies = { usageCollection, + savedObjects: { + createInternalRepository: jest.fn().mockImplementation(() => { + return { + get: () => { + return { + attributes: { + 'ui_open.overview': 10, + 'ui_open.elasticsearch': 20, + 'ui_open.kibana': 15, + 'ui_reindex.close': 1, + 'ui_reindex.open': 4, + 'ui_reindex.start': 2, + 'ui_reindex.stop': 1, + 'ui_reindex.not_defined': 1, + }, + }; + }, + }; + }), + }, elasticsearch: { client: clusterClient, }, @@ -71,6 +91,17 @@ describe('Upgrade Assistant Usage Collector', () => { callClusterStub ); expect(upgradeAssistantStats).toEqual({ + ui_open: { + overview: 10, + elasticsearch: 20, + kibana: 15, + }, + ui_reindex: { + close: 1, + open: 4, + start: 2, + stop: 1, + }, features: { deprecation_logging: { enabled: true, diff --git a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts index c535cd14f104d..56932f5e54b06 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts @@ -5,14 +5,43 @@ * 2.0. */ -import { ElasticsearchClient, ElasticsearchServiceStart } from 'src/core/server'; +import { get } from 'lodash'; +import { + ElasticsearchClient, + ElasticsearchServiceStart, + ISavedObjectsRepository, + SavedObjectsServiceStart, +} from 'src/core/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { UpgradeAssistantTelemetry } from '../../../common/types'; +import { + UPGRADE_ASSISTANT_DOC_ID, + UPGRADE_ASSISTANT_TYPE, + UpgradeAssistantTelemetry, + UpgradeAssistantTelemetrySavedObject, + UpgradeAssistantTelemetrySavedObjectAttributes, +} from '../../../common/types'; import { isDeprecationLogIndexingEnabled, isDeprecationLoggingEnabled, } from '../es_deprecation_logging_apis'; +async function getSavedObjectAttributesFromRepo( + savedObjectsRepository: ISavedObjectsRepository, + docType: string, + docID: string +) { + try { + return ( + await savedObjectsRepository.get( + docType, + docID + ) + ).attributes; + } catch (e) { + return null; + } +} + async function getDeprecationLoggingStatusValue(esClient: ElasticsearchClient): Promise { try { const { body: loggerDeprecationCallResult } = await esClient.cluster.getSettings({ @@ -28,14 +57,58 @@ async function getDeprecationLoggingStatusValue(esClient: ElasticsearchClient): } } -export async function fetchUpgradeAssistantMetrics({ - client: esClient, -}: ElasticsearchServiceStart): Promise { +export async function fetchUpgradeAssistantMetrics( + { client: esClient }: ElasticsearchServiceStart, + savedObjects: SavedObjectsServiceStart +): Promise { + const savedObjectsRepository = savedObjects.createInternalRepository(); + const upgradeAssistantSOAttributes = await getSavedObjectAttributesFromRepo( + savedObjectsRepository, + UPGRADE_ASSISTANT_TYPE, + UPGRADE_ASSISTANT_DOC_ID + ); const deprecationLoggingStatusValue = await getDeprecationLoggingStatusValue( esClient.asInternalUser ); + const getTelemetrySavedObject = ( + upgradeAssistantTelemetrySavedObjectAttrs: UpgradeAssistantTelemetrySavedObjectAttributes | null + ): UpgradeAssistantTelemetrySavedObject => { + const defaultTelemetrySavedObject = { + ui_open: { + overview: 0, + elasticsearch: 0, + kibana: 0, + }, + ui_reindex: { + close: 0, + open: 0, + start: 0, + stop: 0, + }, + }; + + if (!upgradeAssistantTelemetrySavedObjectAttrs) { + return defaultTelemetrySavedObject; + } + + return { + ui_open: { + overview: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_open.overview', 0), + elasticsearch: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_open.elasticsearch', 0), + kibana: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_open.kibana', 0), + }, + ui_reindex: { + close: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_reindex.close', 0), + open: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_reindex.open', 0), + start: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_reindex.start', 0), + stop: get(upgradeAssistantTelemetrySavedObjectAttrs, 'ui_reindex.stop', 0), + }, + } as UpgradeAssistantTelemetrySavedObject; + }; + return { + ...getTelemetrySavedObject(upgradeAssistantSOAttributes), features: { deprecation_logging: { enabled: deprecationLoggingStatusValue, @@ -46,12 +119,14 @@ export async function fetchUpgradeAssistantMetrics({ interface Dependencies { elasticsearch: ElasticsearchServiceStart; + savedObjects: SavedObjectsServiceStart; usageCollection: UsageCollectionSetup; } export function registerUpgradeAssistantUsageCollector({ elasticsearch, usageCollection, + savedObjects, }: Dependencies) { const upgradeAssistantUsageCollector = usageCollection.makeUsageCollector({ @@ -68,8 +143,34 @@ export function registerUpgradeAssistantUsageCollector({ }, }, }, + ui_open: { + elasticsearch: { + type: 'long', + _meta: { + description: 'Number of times a user viewed the list of Elasticsearch deprecations.', + }, + }, + overview: { + type: 'long', + _meta: { + description: 'Number of times a user viewed the Overview page.', + }, + }, + kibana: { + type: 'long', + _meta: { + description: 'Number of times a user viewed the list of Kibana deprecations', + }, + }, + }, + ui_reindex: { + close: { type: 'long' }, + open: { type: 'long' }, + start: { type: 'long' }, + stop: { type: 'long' }, + }, }, - fetch: async () => fetchUpgradeAssistantMetrics(elasticsearch), + fetch: async () => fetchUpgradeAssistantMetrics(elasticsearch, savedObjects), }); usageCollection.registerCollector(upgradeAssistantUsageCollector); diff --git a/x-pack/plugins/upgrade_assistant/server/plugin.ts b/x-pack/plugins/upgrade_assistant/server/plugin.ts index 717f03758f825..2062cf982d15f 100644 --- a/x-pack/plugins/upgrade_assistant/server/plugin.ts +++ b/x-pack/plugins/upgrade_assistant/server/plugin.ts @@ -142,10 +142,11 @@ export class UpgradeAssistantServerPlugin implements Plugin { registerRoutes(dependencies, this.getWorker.bind(this)); if (usageCollection) { - getStartServices().then(([{ elasticsearch }]) => { + getStartServices().then(([{ savedObjects: savedObjectsService, elasticsearch }]) => { registerUpgradeAssistantUsageCollector({ elasticsearch, usageCollection, + savedObjects: savedObjectsService, }); }); } diff --git a/x-pack/plugins/upgrade_assistant/server/routes/register_routes.ts b/x-pack/plugins/upgrade_assistant/server/routes/register_routes.ts index 002f34a489cff..8b63233a124a7 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/register_routes.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/register_routes.ts @@ -12,6 +12,7 @@ import { registerCloudBackupStatusRoutes } from './cloud_backup_status'; import { registerESDeprecationRoutes } from './es_deprecations'; import { registerDeprecationLoggingRoutes } from './deprecation_logging'; import { registerReindexIndicesRoutes } from './reindex_indices'; +import { registerTelemetryRoutes } from './telemetry'; import { registerUpdateSettingsRoute } from './update_index_settings'; import { registerMlSnapshotRoutes } from './ml_snapshots'; import { ReindexWorker } from '../lib/reindexing'; @@ -23,6 +24,7 @@ export function registerRoutes(dependencies: RouteDependencies, getWorker: () => registerESDeprecationRoutes(dependencies); registerDeprecationLoggingRoutes(dependencies); registerReindexIndicesRoutes(dependencies, getWorker); + registerTelemetryRoutes(dependencies); registerUpdateSettingsRoute(dependencies); registerMlSnapshotRoutes(dependencies); // Route for cloud to retrieve the upgrade status for ES and Kibana diff --git a/x-pack/plugins/upgrade_assistant/server/routes/telemetry.test.ts b/x-pack/plugins/upgrade_assistant/server/routes/telemetry.test.ts new file mode 100644 index 0000000000000..578cceb702751 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/server/routes/telemetry.test.ts @@ -0,0 +1,187 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { kibanaResponseFactory } from 'src/core/server'; +import { savedObjectsServiceMock } from 'src/core/server/mocks'; +import { createMockRouter, MockRouter, routeHandlerContextMock } from './__mocks__/routes.mock'; +import { createRequestMock } from './__mocks__/request.mock'; + +jest.mock('../lib/telemetry/es_ui_open_apis', () => ({ + upsertUIOpenOption: jest.fn(), +})); + +jest.mock('../lib/telemetry/es_ui_reindex_apis', () => ({ + upsertUIReindexOption: jest.fn(), +})); + +import { upsertUIOpenOption } from '../lib/telemetry/es_ui_open_apis'; +import { upsertUIReindexOption } from '../lib/telemetry/es_ui_reindex_apis'; +import { registerTelemetryRoutes } from './telemetry'; + +/** + * Since these route callbacks are so thin, these serve simply as integration tests + * to ensure they're wired up to the lib functions correctly. Business logic is tested + * more thoroughly in the lib/telemetry tests. + */ +describe('Upgrade Assistant Telemetry API', () => { + let routeDependencies: any; + let mockRouter: MockRouter; + beforeEach(() => { + mockRouter = createMockRouter(); + routeDependencies = { + getSavedObjectsService: () => savedObjectsServiceMock.create(), + router: mockRouter, + }; + registerTelemetryRoutes(routeDependencies); + }); + afterEach(() => jest.clearAllMocks()); + + describe('PUT /api/upgrade_assistant/stats/ui_open', () => { + it('returns correct payload with single option', async () => { + const returnPayload = { + overview: true, + elasticsearch: false, + kibana: false, + }; + + (upsertUIOpenOption as jest.Mock).mockResolvedValue(returnPayload); + + const resp = await routeDependencies.router.getHandler({ + method: 'put', + pathPattern: '/api/upgrade_assistant/stats/ui_open', + })( + routeHandlerContextMock, + createRequestMock({ body: returnPayload }), + kibanaResponseFactory + ); + + expect(resp.payload).toEqual(returnPayload); + }); + + it('returns correct payload with multiple option', async () => { + const returnPayload = { + overview: true, + elasticsearch: true, + kibana: true, + }; + + (upsertUIOpenOption as jest.Mock).mockResolvedValue(returnPayload); + + const resp = await routeDependencies.router.getHandler({ + method: 'put', + pathPattern: '/api/upgrade_assistant/stats/ui_open', + })( + routeHandlerContextMock, + createRequestMock({ + body: { + overview: true, + elasticsearch: true, + kibana: true, + }, + }), + kibanaResponseFactory + ); + + expect(resp.payload).toEqual(returnPayload); + }); + + it('returns an error if it throws', async () => { + (upsertUIOpenOption as jest.Mock).mockRejectedValue(new Error(`scary error!`)); + + await expect( + routeDependencies.router.getHandler({ + method: 'put', + pathPattern: '/api/upgrade_assistant/stats/ui_open', + })( + routeHandlerContextMock, + createRequestMock({ + body: { + overview: false, + }, + }), + kibanaResponseFactory + ) + ).rejects.toThrowError('scary error!'); + }); + }); + + describe('PUT /api/upgrade_assistant/stats/ui_reindex', () => { + it('returns correct payload with single option', async () => { + const returnPayload = { + close: false, + open: false, + start: true, + stop: false, + }; + + (upsertUIReindexOption as jest.Mock).mockResolvedValue(returnPayload); + + const resp = await routeDependencies.router.getHandler({ + method: 'put', + pathPattern: '/api/upgrade_assistant/stats/ui_reindex', + })( + routeHandlerContextMock, + createRequestMock({ + body: { + overview: false, + }, + }), + kibanaResponseFactory + ); + + expect(resp.payload).toEqual(returnPayload); + }); + + it('returns correct payload with multiple option', async () => { + const returnPayload = { + close: true, + open: true, + start: true, + stop: true, + }; + + (upsertUIReindexOption as jest.Mock).mockResolvedValue(returnPayload); + + const resp = await routeDependencies.router.getHandler({ + method: 'put', + pathPattern: '/api/upgrade_assistant/stats/ui_reindex', + })( + routeHandlerContextMock, + createRequestMock({ + body: { + close: true, + open: true, + start: true, + stop: true, + }, + }), + kibanaResponseFactory + ); + + expect(resp.payload).toEqual(returnPayload); + }); + + it('returns an error if it throws', async () => { + (upsertUIReindexOption as jest.Mock).mockRejectedValue(new Error(`scary error!`)); + + await expect( + routeDependencies.router.getHandler({ + method: 'put', + pathPattern: '/api/upgrade_assistant/stats/ui_reindex', + })( + routeHandlerContextMock, + createRequestMock({ + body: { + start: false, + }, + }), + kibanaResponseFactory + ) + ).rejects.toThrowError('scary error!'); + }); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/server/routes/telemetry.ts b/x-pack/plugins/upgrade_assistant/server/routes/telemetry.ts new file mode 100644 index 0000000000000..d083b38c7c240 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/server/routes/telemetry.ts @@ -0,0 +1,64 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { API_BASE_PATH } from '../../common/constants'; +import { upsertUIOpenOption } from '../lib/telemetry/es_ui_open_apis'; +import { upsertUIReindexOption } from '../lib/telemetry/es_ui_reindex_apis'; +import { RouteDependencies } from '../types'; + +export function registerTelemetryRoutes({ router, getSavedObjectsService }: RouteDependencies) { + router.put( + { + path: `${API_BASE_PATH}/stats/ui_open`, + validate: { + body: schema.object({ + overview: schema.boolean({ defaultValue: false }), + elasticsearch: schema.boolean({ defaultValue: false }), + kibana: schema.boolean({ defaultValue: false }), + }), + }, + }, + async (ctx, request, response) => { + const { elasticsearch, overview, kibana } = request.body; + return response.ok({ + body: await upsertUIOpenOption({ + savedObjects: getSavedObjectsService(), + elasticsearch, + overview, + kibana, + }), + }); + } + ); + + router.put( + { + path: `${API_BASE_PATH}/stats/ui_reindex`, + validate: { + body: schema.object({ + close: schema.boolean({ defaultValue: false }), + open: schema.boolean({ defaultValue: false }), + start: schema.boolean({ defaultValue: false }), + stop: schema.boolean({ defaultValue: false }), + }), + }, + }, + async (ctx, request, response) => { + const { close, open, start, stop } = request.body; + return response.ok({ + body: await upsertUIReindexOption({ + savedObjects: getSavedObjectsService(), + close, + open, + start, + stop, + }), + }); + } + ); +} diff --git a/x-pack/plugins/upgrade_assistant/server/saved_object_types/telemetry_saved_object_type.ts b/x-pack/plugins/upgrade_assistant/server/saved_object_types/telemetry_saved_object_type.ts index cb3fbcaef59b7..42d5d339dd050 100644 --- a/x-pack/plugins/upgrade_assistant/server/saved_object_types/telemetry_saved_object_type.ts +++ b/x-pack/plugins/upgrade_assistant/server/saved_object_types/telemetry_saved_object_type.ts @@ -15,6 +15,42 @@ export const telemetrySavedObjectType: SavedObjectsType = { namespaceType: 'agnostic', mappings: { properties: { + ui_open: { + properties: { + overview: { + type: 'long', + null_value: 0, + }, + elasticsearch: { + type: 'long', + null_value: 0, + }, + kibana: { + type: 'long', + null_value: 0, + }, + }, + }, + ui_reindex: { + properties: { + close: { + type: 'long', + null_value: 0, + }, + open: { + type: 'long', + null_value: 0, + }, + start: { + type: 'long', + null_value: 0, + }, + stop: { + type: 'long', + null_value: 0, + }, + }, + }, features: { properties: { deprecation_logging: { From 40601a647db5236c2107c4a026bd535fd92c1864 Mon Sep 17 00:00:00 2001 From: Tre Date: Fri, 1 Oct 2021 18:24:47 -0400 Subject: [PATCH 47/51] [7.x] [Archive Migration] xpack..saved_objects_management/spaces_integration (#113047) (#113643) * [Archive Migration] xpack..saved_objects_management/spaces_integration (#113047) # Conflicts: # x-pack/test/functional/apps/saved_objects_management/spaces_integration.ts # x-pack/test/functional/es_archives/saved_objects_management/spaces_integration/data.json # x-pack/test/functional/es_archives/saved_objects_management/spaces_integration/mappings.json * Drop unused symbol --- .../spaces_integration.ts | 13 +++--- .../spaces_integration.json | 44 +++++++++++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 x-pack/test/functional/fixtures/kbn_archiver/saved_objects_management/spaces_integration.json diff --git a/x-pack/test/functional/apps/saved_objects_management/spaces_integration.ts b/x-pack/test/functional/apps/saved_objects_management/spaces_integration.ts index 28d04c1f9c54c..757bc96e6e27a 100644 --- a/x-pack/test/functional/apps/saved_objects_management/spaces_integration.ts +++ b/x-pack/test/functional/apps/saved_objects_management/spaces_integration.ts @@ -13,7 +13,6 @@ const getSpacePrefix = (spaceId: string) => { }; export default function ({ getPageObjects, getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); const testSubjects = getService('testSubjects'); const PageObjects = getPageObjects([ 'common', @@ -22,20 +21,22 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'spaceSelector', 'settings', ]); + const kibanaServer = getService('kibanaServer'); + const spacesService = getService('spaces'); const spaceId = 'space_1'; describe('spaces integration', () => { before(async () => { - await esArchiver.load( - 'x-pack/test/functional/es_archives/saved_objects_management/spaces_integration' + await spacesService.create({ id: spaceId, name: spaceId }); + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/saved_objects_management/spaces_integration', + { space: spaceId } ); }); after(async () => { - await esArchiver.unload( - 'x-pack/test/functional/es_archives/saved_objects_management/spaces_integration' - ); + await spacesService.delete(spaceId); }); beforeEach(async () => { diff --git a/x-pack/test/functional/fixtures/kbn_archiver/saved_objects_management/spaces_integration.json b/x-pack/test/functional/fixtures/kbn_archiver/saved_objects_management/spaces_integration.json new file mode 100644 index 0000000000000..4a7ab6d406736 --- /dev/null +++ b/x-pack/test/functional/fixtures/kbn_archiver/saved_objects_management/spaces_integration.json @@ -0,0 +1,44 @@ +{ + "attributes": { + "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", + "timeFieldName": "@timestamp", + "title": "logstash-*" + }, + "coreMigrationVersion": "7.16.0", + "id": "logstash-*", + "migrationVersion": { + "index-pattern": "7.11.0" + }, + "references": [], + "type": "index-pattern", + "updated_at": "2021-09-29T14:44:14.268Z", + "version": "WzExLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "A Pie", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"A Pie\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"dimensions\":{\"metric\":{\"accessor\":0,\"format\":{\"id\":\"number\"},\"params\":{},\"aggType\":\"count\"}},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}" + }, + "coreMigrationVersion": "7.16.0", + "id": "75c3e060-1e7c-11e9-8488-65449e65d0ed", + "migrationVersion": { + "visualization": "7.14.0" + }, + "references": [ + { + "id": "logstash-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2021-09-29T14:45:14.428Z", + "version": "WzEzLDFd" +} \ No newline at end of file From 7e7eb63703126855aae6fecc36e31e6ba31a0c34 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 18:28:23 -0400 Subject: [PATCH 48/51] [Cases] [104932] Remove newline characters from summary field and add tests (#113571) (#113668) * [104932] Remove newline characters from summary field and add tests * PR fix Co-authored-by: Kristof-Pierre Cummings Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Kristof C Co-authored-by: Kristof-Pierre Cummings --- .../builtin_action_types/jira/service.test.ts | 51 +++++++++++++++++++ .../builtin_action_types/jira/service.ts | 10 +++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts index af518f0cebc07..2300143925b1e 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts @@ -316,6 +316,57 @@ describe('Jira service', () => { }); }); + test('removes newline characters and trialing spaces from summary', async () => { + requestMock.mockImplementationOnce(() => ({ + data: { + capabilities: { + navigation: 'https://siem-kibana.atlassian.net/rest/capabilities/navigation', + }, + }, + })); + + // getIssueType mocks + requestMock.mockImplementationOnce(() => issueTypesResponse); + + // getIssueType mocks + requestMock.mockImplementationOnce(() => ({ + data: { id: '1', key: 'CK-1', fields: { summary: 'test', description: 'description' } }, + })); + + requestMock.mockImplementationOnce(() => ({ + data: { id: '1', key: 'CK-1', fields: { created: '2020-04-27T10:59:46.202Z' } }, + })); + + await service.createIncident({ + incident: { + summary: 'title \n \n \n howdy \r \r \n \r test', + description: 'desc', + labels: [], + priority: 'High', + issueType: null, + parent: null, + }, + }); + + expect(requestMock).toHaveBeenCalledWith({ + axios, + url: 'https://siem-kibana.atlassian.net/rest/api/2/issue', + logger, + method: 'post', + configurationUtilities, + data: { + fields: { + summary: 'title, howdy, test', + description: 'desc', + project: { key: 'CK' }, + issuetype: { id: '10006' }, + labels: [], + priority: { name: 'High' }, + }, + }, + }); + }); + test('it should call request with correct arguments', async () => { requestMock.mockImplementation(() => ({ data: { diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts index 063895c7eb5cd..be0240e705a65 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts @@ -6,6 +6,7 @@ */ import axios from 'axios'; +import { isEmpty } from 'lodash'; import { Logger } from '../../../../../../src/core/server'; import { @@ -76,7 +77,7 @@ export const createExternalService = ( const createFields = (key: string, incident: Incident): Fields => { let fields: Fields = { - summary: incident.summary, + summary: trimAndRemoveNewlines(incident.summary), project: { key }, }; @@ -103,6 +104,13 @@ export const createExternalService = ( return fields; }; + const trimAndRemoveNewlines = (str: string) => + str + .split(/[\n\r]/gm) + .map((item) => item.trim()) + .filter((item) => !isEmpty(item)) + .join(', '); + const createErrorMessage = (errorResponse: ResponseError | string | null | undefined): string => { if (errorResponse == null) { return ''; From fe00c134d969b7c53e989844ac31b60a2f77c0a0 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 19:13:42 -0400 Subject: [PATCH 49/51] [Buildkite] Pull Request pipeline (#112029) (#113683) Co-authored-by: Brian Seeders --- .buildkite/pipelines/es_snapshots/verify.yml | 2 +- .buildkite/pipelines/hourly.yml | 2 +- .../pipelines/pull_request/apm_cypress.yml | 11 ++ .buildkite/pipelines/pull_request/base.yml | 155 ++++++++++++++++++ .../pipelines/pull_request/post_build.yml | 6 + .../pull_request/security_solution.yml | 11 ++ .buildkite/scripts/common/env.sh | 10 +- .buildkite/scripts/common/util.sh | 12 ++ .buildkite/scripts/lifecycle/post_build.sh | 9 + .../pipelines/pull_request/pipeline.js | 84 ++++++++++ .../pipelines/pull_request/pipeline.sh | 5 + .../scripts/steps/functional/apm_cypress.sh | 14 ++ .../steps/functional/security_solution.sh | 17 ++ .../steps/storybooks/build_and_upload.js | 6 + 14 files changed, 341 insertions(+), 3 deletions(-) create mode 100644 .buildkite/pipelines/pull_request/apm_cypress.yml create mode 100644 .buildkite/pipelines/pull_request/base.yml create mode 100644 .buildkite/pipelines/pull_request/post_build.yml create mode 100644 .buildkite/pipelines/pull_request/security_solution.yml create mode 100644 .buildkite/scripts/pipelines/pull_request/pipeline.js create mode 100755 .buildkite/scripts/pipelines/pull_request/pipeline.sh create mode 100755 .buildkite/scripts/steps/functional/apm_cypress.sh create mode 100755 .buildkite/scripts/steps/functional/security_solution.sh diff --git a/.buildkite/pipelines/es_snapshots/verify.yml b/.buildkite/pipelines/es_snapshots/verify.yml index 61212e1fcf0a8..b9aa0e0e3727a 100755 --- a/.buildkite/pipelines/es_snapshots/verify.yml +++ b/.buildkite/pipelines/es_snapshots/verify.yml @@ -19,7 +19,7 @@ steps: - command: .buildkite/scripts/steps/build_kibana.sh label: Build Kibana Distribution and Plugins agents: - queue: c2-8 + queue: c2-16 key: build if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''" diff --git a/.buildkite/pipelines/hourly.yml b/.buildkite/pipelines/hourly.yml index 279c8cf96bfe3..7aa8528dc3d9a 100644 --- a/.buildkite/pipelines/hourly.yml +++ b/.buildkite/pipelines/hourly.yml @@ -9,7 +9,7 @@ steps: - command: .buildkite/scripts/steps/build_kibana.sh label: Build Kibana Distribution and Plugins agents: - queue: c2-8 + queue: c2-16 key: build if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''" diff --git a/.buildkite/pipelines/pull_request/apm_cypress.yml b/.buildkite/pipelines/pull_request/apm_cypress.yml new file mode 100644 index 0000000000000..8dcdc6c4b5f39 --- /dev/null +++ b/.buildkite/pipelines/pull_request/apm_cypress.yml @@ -0,0 +1,11 @@ +steps: + - command: .buildkite/scripts/steps/functional/apm_cypress.sh + label: 'APM Cypress Tests' + agents: + queue: ci-group-6 + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '*' + limit: 1 diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml new file mode 100644 index 0000000000000..1da3f654b7253 --- /dev/null +++ b/.buildkite/pipelines/pull_request/base.yml @@ -0,0 +1,155 @@ +steps: + - command: .buildkite/scripts/lifecycle/pre_build.sh + label: Pre-Build + + - wait + + - command: .buildkite/scripts/steps/build_kibana.sh + label: Build Kibana Distribution and Plugins + agents: + queue: c2-16 + key: build + if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''" + + - command: .buildkite/scripts/steps/functional/xpack_cigroup.sh + label: 'Default CI Group' + parallelism: 13 + agents: + queue: ci-group-6 + depends_on: build + timeout_in_minutes: 120 + key: default-cigroup + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: CI_GROUP=Docker .buildkite/scripts/steps/functional/xpack_cigroup.sh + label: 'Docker CI Group' + agents: + queue: ci-group-6 + depends_on: build + timeout_in_minutes: 120 + key: default-cigroup-docker + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/oss_cigroup.sh + label: 'OSS CI Group' + parallelism: 11 + agents: + queue: ci-group-4d + depends_on: build + timeout_in_minutes: 120 + key: oss-cigroup + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/oss_accessibility.sh + label: 'OSS Accessibility Tests' + agents: + queue: ci-group-4d + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/xpack_accessibility.sh + label: 'Default Accessibility Tests' + agents: + queue: ci-group-6 + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/oss_firefox.sh + label: 'OSS Firefox Tests' + agents: + queue: ci-group-4d + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/xpack_firefox.sh + label: 'Default Firefox Tests' + agents: + queue: ci-group-6 + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/oss_misc.sh + label: 'OSS Misc Functional Tests' + agents: + queue: ci-group-4d + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/xpack_saved_object_field_metrics.sh + label: 'Saved Object Field Metrics' + agents: + queue: ci-group-6 + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/test/jest_integration.sh + label: 'Jest Integration Tests' + agents: + queue: n2-4 + timeout_in_minutes: 120 + key: jest-integration + + - command: .buildkite/scripts/steps/test/api_integration.sh + label: 'API Integration Tests' + agents: + queue: n2-2 + timeout_in_minutes: 120 + key: api-integration + + - command: .buildkite/scripts/steps/test/jest.sh + label: 'Jest Tests' + agents: + queue: c2-16 + timeout_in_minutes: 120 + key: jest + + - command: .buildkite/scripts/steps/lint.sh + label: 'Linting' + agents: + queue: n2-2 + key: linting + + - command: .buildkite/scripts/steps/checks.sh + label: 'Checks' + agents: + queue: c2-4 + key: checks + + - command: .buildkite/scripts/steps/storybooks/build_and_upload.sh + label: 'Build Storybooks' + agents: + queue: c2-4 + key: storybooks diff --git a/.buildkite/pipelines/pull_request/post_build.yml b/.buildkite/pipelines/pull_request/post_build.yml new file mode 100644 index 0000000000000..4f252bf8abc11 --- /dev/null +++ b/.buildkite/pipelines/pull_request/post_build.yml @@ -0,0 +1,6 @@ +steps: + - wait: ~ + continue_on_failure: true + + - command: .buildkite/scripts/lifecycle/post_build.sh + label: Post-Build diff --git a/.buildkite/pipelines/pull_request/security_solution.yml b/.buildkite/pipelines/pull_request/security_solution.yml new file mode 100644 index 0000000000000..974469a700715 --- /dev/null +++ b/.buildkite/pipelines/pull_request/security_solution.yml @@ -0,0 +1,11 @@ +steps: + - command: .buildkite/scripts/steps/functional/security_solution.sh + label: 'Security Solution Tests' + agents: + queue: ci-group-6 + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '*' + limit: 1 diff --git a/.buildkite/scripts/common/env.sh b/.buildkite/scripts/common/env.sh index 282af74bbe18f..89121581c75d1 100755 --- a/.buildkite/scripts/common/env.sh +++ b/.buildkite/scripts/common/env.sh @@ -45,12 +45,20 @@ if is_pr; then export ghprbActualCommit="$BUILDKITE_COMMIT" export BUILD_URL="$BUILDKITE_BUILD_URL" - # set_git_merge_base # TODO for PRs + set_git_merge_base + + # For backwards compatibility + export PR_MERGE_BASE="$GITHUB_PR_MERGE_BASE" + export PR_TARGET_BRANCH="$GITHUB_PR_TARGET_BRANCH" else export ELASTIC_APM_ACTIVE=true export CHECKS_REPORTER_ACTIVE=false fi +# These are for backwards-compatibility +export GIT_COMMIT="$BUILDKITE_COMMIT" +export GIT_BRANCH="$BUILDKITE_BRANCH" + export FLEET_PACKAGE_REGISTRY_PORT=6104 export TEST_CORS_SERVER_PORT=6105 diff --git a/.buildkite/scripts/common/util.sh b/.buildkite/scripts/common/util.sh index d536f1a37acfd..a884a147577c9 100755 --- a/.buildkite/scripts/common/util.sh +++ b/.buildkite/scripts/common/util.sh @@ -74,3 +74,15 @@ retry() { fi done } + +set_git_merge_base() { + GITHUB_PR_MERGE_BASE="$(buildkite-agent meta-data get merge-base --default '')" + + if [[ ! "$GITHUB_PR_MERGE_BASE" ]]; then + git fetch origin "$GITHUB_PR_TARGET_BRANCH" + GITHUB_PR_MERGE_BASE="$(git merge-base HEAD FETCH_HEAD)" + buildkite-agent meta-data set merge-base "$GITHUB_PR_MERGE_BASE" + fi + + export GITHUB_PR_MERGE_BASE +} diff --git a/.buildkite/scripts/lifecycle/post_build.sh b/.buildkite/scripts/lifecycle/post_build.sh index 4577c1a9fcad4..6052814cc5f09 100755 --- a/.buildkite/scripts/lifecycle/post_build.sh +++ b/.buildkite/scripts/lifecycle/post_build.sh @@ -8,3 +8,12 @@ export BUILD_SUCCESSFUL "$(dirname "${0}")/commit_status_complete.sh" node "$(dirname "${0}")/ci_stats_complete.js" + +if [[ "$GITHUB_PR_NUMBER" ]]; then + DOCS_CHANGES_URL="https://kibana_$GITHUB_PR_NUMBER}.docs-preview.app.elstc.co/diff" + DOCS_CHANGES=$(curl --connect-timeout 10 -m 10 -sf "$DOCS_CHANGES_URL" || echo '') + + if [[ "$DOCS_CHANGES" && "$DOCS_CHANGES" != "There aren't any differences!" ]]; then + buildkite-agent meta-data set pr_comment:docs_changes:head "* [Documentation Changes](${DOCS_CHANGES_URL})" + fi +fi diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.js b/.buildkite/scripts/pipelines/pull_request/pipeline.js new file mode 100644 index 0000000000000..068de9917c213 --- /dev/null +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.js @@ -0,0 +1,84 @@ +const execSync = require('child_process').execSync; +const fs = require('fs'); +const { areChangesSkippable, doAnyChangesMatch } = require('kibana-buildkite-library'); + +const SKIPPABLE_PATHS = [ + /^docs\//, + /^rfcs\//, + /^.ci\/.+\.yml$/, + /^.ci\/es-snapshots\//, + /^.ci\/pipeline-library\//, + /^.ci\/Jenkinsfile_[^\/]+$/, + /^\.github\//, + /\.md$/, + /^\.backportrc\.json$/, +]; + +const REQUIRED_PATHS = [ + // this file is auto-generated and changes to it need to be validated with CI + /^docs\/developer\/plugin-list.asciidoc$/, + // don't skip CI on prs with changes to plugin readme files /i is for case-insensitive matching + /\/plugins\/[^\/]+\/readme\.(md|asciidoc)$/i, +]; + +const getPipeline = (filename, removeSteps = true) => { + const str = fs.readFileSync(filename).toString(); + return removeSteps ? str.replace(/^steps:/, '') : str; +}; + +const uploadPipeline = (pipelineContent) => { + const str = + typeof pipelineContent === 'string' ? pipelineContent : JSON.stringify(pipelineContent); + + execSync('buildkite-agent pipeline upload', { + input: str, + stdio: ['pipe', 'inherit', 'inherit'], + }); +}; + +(async () => { + try { + const skippable = await areChangesSkippable(SKIPPABLE_PATHS, REQUIRED_PATHS); + + if (skippable) { + console.log('All changes in PR are skippable. Skipping CI.'); + + // Since we skip everything, including post-build, we need to at least make sure the commit status gets set + execSync('BUILD_SUCCESSFUL=true .buildkite/scripts/lifecycle/commit_status_complete.sh', { + stdio: 'inherit', + }); + process.exit(0); + } + + const pipeline = []; + + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/base.yml', false)); + + if ( + await doAnyChangesMatch([ + /^x-pack\/plugins\/security_solution/, + /^x-pack\/test\/security_solution_cypress/, + /^x-pack\/plugins\/triggers_actions_ui\/public\/application\/sections\/action_connector_form/, + /^x-pack\/plugins\/triggers_actions_ui\/public\/application\/context\/actions_connectors_context\.tsx/, + ]) + ) { + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/security_solution.yml')); + } + + // Disabled for now, these are failing/disabled in Jenkins currently as well + // if ( + // await doAnyChangesMatch([ + // /^x-pack\/plugins\/apm/, + // ]) + // ) { + // pipeline.push(getPipeline('.buildkite/pipelines/pull_request/apm_cypress.yml')); + // } + + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/post_build.yml')); + + uploadPipeline(pipeline.join('\n')); + } catch (ex) { + console.error('PR pipeline generation error', ex.message); + process.exit(1); + } +})(); diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.sh b/.buildkite/scripts/pipelines/pull_request/pipeline.sh new file mode 100755 index 0000000000000..02be2acdf8588 --- /dev/null +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -euo pipefail + +node .buildkite/scripts/pipelines/pull_request/pipeline.js diff --git a/.buildkite/scripts/steps/functional/apm_cypress.sh b/.buildkite/scripts/steps/functional/apm_cypress.sh new file mode 100755 index 0000000000000..800f22c78d14c --- /dev/null +++ b/.buildkite/scripts/steps/functional/apm_cypress.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/steps/functional/common.sh + +export JOB=kibana-apm-cypress + +echo "--- APM Cypress Tests" + +cd "$XPACK_DIR" + +checks-reporter-with-killswitch "APM Cypress Tests" \ + node plugins/apm/scripts/test/e2e.js diff --git a/.buildkite/scripts/steps/functional/security_solution.sh b/.buildkite/scripts/steps/functional/security_solution.sh new file mode 100755 index 0000000000000..9b2bfc7207a95 --- /dev/null +++ b/.buildkite/scripts/steps/functional/security_solution.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/steps/functional/common.sh + +export JOB=kibana-security-solution-chrome + +echo "--- Security Solution tests (Chrome)" + +cd "$XPACK_DIR" + +checks-reporter-with-killswitch "Security Solution Cypress Tests (Chrome)" \ + node scripts/functional_tests \ + --debug --bail \ + --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ + --config test/security_solution_cypress/cli_config.ts diff --git a/.buildkite/scripts/steps/storybooks/build_and_upload.js b/.buildkite/scripts/steps/storybooks/build_and_upload.js index 49e36d2126cd4..0fdf24d87ffad 100644 --- a/.buildkite/scripts/steps/storybooks/build_and_upload.js +++ b/.buildkite/scripts/steps/storybooks/build_and_upload.js @@ -98,6 +98,12 @@ const upload = () => { gsutil -q -m cp -r -z js,css,html,json,map,txt,svg '*' 'gs://${STORYBOOK_BUCKET}/${STORYBOOK_DIRECTORY}/${process.env.BUILDKITE_COMMIT}/' gsutil -h "Cache-Control:no-cache, max-age=0, no-transform" cp -z html 'index.html' 'gs://${STORYBOOK_BUCKET}/${STORYBOOK_DIRECTORY}/latest/' `); + + if (process.env.BUILDKITE_PULL_REQUEST && process.env.BUILDKITE_PULL_REQUEST !== 'false') { + exec( + `buildkite-agent meta-data set pr_comment:storybooks:head '* [Storybooks Preview](${STORYBOOK_BASE_URL})'` + ); + } } finally { process.chdir(originalDirectory); } From 53fa6f53a005b244e70b5a696a8742d2fd1706f0 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 1 Oct 2021 19:51:55 -0400 Subject: [PATCH 50/51] [buildkite] Fix unbound variable in post_build script (#113687) (#113688) Co-authored-by: Brian Seeders --- .buildkite/scripts/lifecycle/post_build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildkite/scripts/lifecycle/post_build.sh b/.buildkite/scripts/lifecycle/post_build.sh index 6052814cc5f09..35e5a6006ee24 100755 --- a/.buildkite/scripts/lifecycle/post_build.sh +++ b/.buildkite/scripts/lifecycle/post_build.sh @@ -9,7 +9,7 @@ export BUILD_SUCCESSFUL node "$(dirname "${0}")/ci_stats_complete.js" -if [[ "$GITHUB_PR_NUMBER" ]]; then +if [[ "${GITHUB_PR_NUMBER:-}" ]]; then DOCS_CHANGES_URL="https://kibana_$GITHUB_PR_NUMBER}.docs-preview.app.elstc.co/diff" DOCS_CHANGES=$(curl --connect-timeout 10 -m 10 -sf "$DOCS_CHANGES_URL" || echo '') From 44b1a2e4cfd07bd97d5ed67d6a015e218e0ac242 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Sun, 3 Oct 2021 18:34:03 -0400 Subject: [PATCH 51/51] [ci-stats] Local metrics fixes (#113492) (#113710) * Rename `{ group: 'yarn start', id: 'started' }` to `{ group: 'scripts/kibana', id: 'dev server started' }` for consistency * Rename `{ group: '@kbn/optimizer' }` to `{ group: 'scripts/build_kibana_platform_plugins' }` for consistency * Include email for Elastic employees * Standardize on Unix paths * Set `subProcess: true` if the timing is already captured by a parent. * Move nestedTiming to global and use normalize-path Co-authored-by: Tyler Smalley --- packages/kbn-cli-dev-mode/src/cli_dev_mode.ts | 6 +-- .../ci_stats_reporter/ci_stats_reporter.ts | 20 +++++--- packages/kbn-dev-utils/src/run/metrics.ts | 49 ++++++++++--------- .../src/report_optimizer_timings.ts | 2 +- packages/kbn-pm/dist/index.js | 18 ++++--- packages/kbn-pm/src/commands/bootstrap.ts | 5 +- packages/kbn-pm/src/run.ts | 2 + 7 files changed, 58 insertions(+), 44 deletions(-) diff --git a/packages/kbn-cli-dev-mode/src/cli_dev_mode.ts b/packages/kbn-cli-dev-mode/src/cli_dev_mode.ts index a681f1ff2e4cd..28f38592ff3c4 100644 --- a/packages/kbn-cli-dev-mode/src/cli_dev_mode.ts +++ b/packages/kbn-cli-dev-mode/src/cli_dev_mode.ts @@ -289,8 +289,8 @@ export class CliDevMode { await reporter.timings({ timings: [ { - group: 'yarn start', - id: 'started', + group: 'scripts/kibana', + id: 'dev server started', ms: Date.now() - this.startTime!, meta: { success }, }, @@ -313,7 +313,7 @@ export class CliDevMode { await reporter.timings({ timings: [ { - group: 'yarn start', + group: 'scripts/kibana', id: 'dev server restart', ms, meta: { diff --git a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts index 3e9db78c2630c..4d6ea646b2ab1 100644 --- a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts +++ b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts @@ -90,20 +90,24 @@ export class CiStatsReporter { this.log.debug(e.message); } + const isElasticCommitter = email && email.endsWith('@elastic.co') ? true : false; + const defaultMetadata = { - osPlatform: Os.platform(), - osRelease: Os.release(), - osArch: Os.arch(), + committerHash: email + ? crypto.createHash('sha256').update(email).digest('hex').substring(0, 20) + : undefined, cpuCount: Os.cpus()?.length, cpuModel: Os.cpus()[0]?.model, cpuSpeed: Os.cpus()[0]?.speed, + email: isElasticCommitter ? email : undefined, freeMem: Os.freemem(), - totalMem: Os.totalmem(), - committerHash: email - ? crypto.createHash('sha256').update(email).digest('hex').substring(0, 20) - : undefined, - isElasticCommitter: email ? email.endsWith('@elastic.co') : undefined, + isElasticCommitter, kibanaUuid, + nestedTiming: process.env.CI_STATS_NESTED_TIMING ? true : false, + osArch: Os.arch(), + osPlatform: Os.platform(), + osRelease: Os.release(), + totalMem: Os.totalmem(), }; this.log.debug('CIStatsReporter committerHash: %s', defaultMetadata.committerHash); diff --git a/packages/kbn-dev-utils/src/run/metrics.ts b/packages/kbn-dev-utils/src/run/metrics.ts index 7d60bccee3a75..90a005bfc64dd 100644 --- a/packages/kbn-dev-utils/src/run/metrics.ts +++ b/packages/kbn-dev-utils/src/run/metrics.ts @@ -8,6 +8,7 @@ import path from 'path'; import { REPO_ROOT } from '@kbn/utils'; +import normalizePath from 'normalize-path'; import { CiStatsReporter } from '../ci_stats_reporter'; import { ToolingLog } from '../tooling_log'; @@ -23,39 +24,39 @@ export class Metrics { this.reporter = CiStatsReporter.fromEnv(log); this.meta = new Map(); this.startTime = Date.now(); - this.filePath = path.relative(REPO_ROOT, process.argv[1]).replace('.js', ''); + + // standardize to unix path + this.filePath = normalizePath(path.relative(REPO_ROOT, process.argv[1]).replace('.js', '')); + } + + createTiming(meta: object, command?: string) { + return { + group: `${command ? `${this.filePath} ${command}` : this.filePath}`, + id: 'total', + ms: Date.now() - this.startTime, + meta: { + nestedTiming: process.env.CI_STATS_NESTED_TIMING, + ...Object.fromEntries(this.meta), + ...meta, + }, + }; + } + + async reportCancelled(command?: string) { + return await this.reporter.timings({ + timings: [this.createTiming({ cancelled: true }, command)], + }); } async reportSuccess(command?: string) { return await this.reporter.timings({ - timings: [ - { - group: `${command ? `${this.filePath} ${command}` : this.filePath}`, - id: 'total', - ms: Date.now() - this.startTime, - meta: { - success: true, - ...Object.fromEntries(this.meta), - }, - }, - ], + timings: [this.createTiming({ success: true }, command)], }); } async reportError(errorMessage?: string, command?: string) { return await this.reporter.timings({ - timings: [ - { - group: `${command ? `${this.filePath} ${command}` : this.filePath}`, - id: 'total', - ms: Date.now() - this.startTime, - meta: { - success: false, - errorMessage, - ...Object.fromEntries(this.meta), - }, - }, - ], + timings: [this.createTiming({ success: false, errorMessage }, command)], }); } } diff --git a/packages/kbn-optimizer/src/report_optimizer_timings.ts b/packages/kbn-optimizer/src/report_optimizer_timings.ts index 094838a21a0be..e57d0b9a641c6 100644 --- a/packages/kbn-optimizer/src/report_optimizer_timings.ts +++ b/packages/kbn-optimizer/src/report_optimizer_timings.ts @@ -46,7 +46,7 @@ export function reportOptimizerTimings(log: ToolingLog, config: OptimizerConfig) await reporter.timings({ timings: [ { - group: '@kbn/optimizer', + group: 'scripts/build_kibana_platform_plugins', id: 'total', ms: time, meta: { diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 3180347975cf3..2dfda383ea601 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -9108,18 +9108,21 @@ class CiStatsReporter { this.log.debug(e.message); } + const isElasticCommitter = email && email.endsWith('@elastic.co') ? true : false; const defaultMetadata = { - osPlatform: _os.default.platform(), - osRelease: _os.default.release(), - osArch: _os.default.arch(), + committerHash: email ? _crypto.default.createHash('sha256').update(email).digest('hex').substring(0, 20) : undefined, cpuCount: (_Os$cpus = _os.default.cpus()) === null || _Os$cpus === void 0 ? void 0 : _Os$cpus.length, cpuModel: (_Os$cpus$ = _os.default.cpus()[0]) === null || _Os$cpus$ === void 0 ? void 0 : _Os$cpus$.model, cpuSpeed: (_Os$cpus$2 = _os.default.cpus()[0]) === null || _Os$cpus$2 === void 0 ? void 0 : _Os$cpus$2.speed, + email: isElasticCommitter ? email : undefined, freeMem: _os.default.freemem(), - totalMem: _os.default.totalmem(), - committerHash: email ? _crypto.default.createHash('sha256').update(email).digest('hex').substring(0, 20) : undefined, - isElasticCommitter: email ? email.endsWith('@elastic.co') : undefined, - kibanaUuid + isElasticCommitter, + kibanaUuid, + nestedTiming: process.env.CI_STATS_NESTED_TIMING ? true : false, + osArch: _os.default.arch(), + osPlatform: _os.default.platform(), + osRelease: _os.default.release(), + totalMem: _os.default.totalmem() }; this.log.debug('CIStatsReporter committerHash: %s', defaultMetadata.committerHash); return await this.req({ @@ -60705,6 +60708,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope +process.env.CI_STATS_NESTED_TIMING = 'true'; async function runCommand(command, config) { const runStartTime = Date.now(); let kbn; diff --git a/packages/kbn-pm/src/commands/bootstrap.ts b/packages/kbn-pm/src/commands/bootstrap.ts index f8f209fb90de1..95eeeb329da3d 100644 --- a/packages/kbn-pm/src/commands/bootstrap.ts +++ b/packages/kbn-pm/src/commands/bootstrap.ts @@ -145,7 +145,10 @@ export const BootstrapCommand: ICommand = { upstreamBranch: kbn.kibanaProject.json.branch, // prevent loading @kbn/utils by passing null kibanaUuid: kbn.getUuid() || null, - timings: timings.map((t) => ({ group: 'scripts/kbn bootstrap', ...t })), + timings: timings.map((t) => ({ + group: 'scripts/kbn bootstrap', + ...t, + })), }); }, }; diff --git a/packages/kbn-pm/src/run.ts b/packages/kbn-pm/src/run.ts index ae3669ff3b16b..41d26e26a3c29 100644 --- a/packages/kbn-pm/src/run.ts +++ b/packages/kbn-pm/src/run.ts @@ -15,6 +15,8 @@ import { buildProjectGraph } from './utils/projects'; import { renderProjectsTree } from './utils/projects_tree'; import { Kibana } from './utils/kibana'; +process.env.CI_STATS_NESTED_TIMING = 'true'; + export async function runCommand(command: ICommand, config: Omit) { const runStartTime = Date.now(); let kbn;