From c495093f76e3ee899e9298970811b82e0627745f Mon Sep 17 00:00:00 2001 From: Constance Date: Thu, 21 Jan 2021 14:39:14 -0800 Subject: [PATCH 01/62] [App Search] Move generateEnginePath out from EngineLogic values to its own helper (#89022) * [Feedback] Move generateEnginePath to its own standalone helper - instead of living inside EngineLogic.values - I forgot Kea lets us do this now! * Update all components using generateEngineRouter to import helper directly --- .../app_search/__mocks__/engine_logic.mock.ts | 12 ++++---- .../analytics/analytics_router.test.tsx | 4 +-- .../components/analytics/analytics_router.tsx | 5 +--- .../document_creation_buttons.test.tsx | 5 ++-- .../document_creation_buttons.tsx | 5 ++-- .../documents/document_detail_logic.ts | 6 ++-- .../components/engine/engine_logic.test.ts | 23 --------------- .../components/engine/engine_logic.ts | 11 -------- .../components/engine/engine_nav.tsx | 3 +- .../app_search/components/engine/index.ts | 1 + .../components/engine/utils.test.ts | 28 +++++++++++++++++++ .../app_search/components/engine/utils.ts | 17 +++++++++++ .../components/recent_api_logs.test.tsx | 4 +-- .../components/recent_api_logs.tsx | 5 +--- .../components/total_charts.test.tsx | 3 +- .../components/total_charts.tsx | 3 +- 16 files changed, 65 insertions(+), 70 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/utils.test.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/utils.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/engine_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/engine_logic.mock.ts index 5c327f64d7775..6326a41c1d2ca 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/engine_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/engine_logic.mock.ts @@ -8,16 +8,14 @@ import { generatePath } from 'react-router-dom'; export const mockEngineValues = { engineName: 'some-engine', - // Note: using getters allows us to use `this`, which lets tests - // override engineName and still generate correct engine names - get generateEnginePath() { - return jest.fn((path, pathParams = {}) => - generatePath(path, { engineName: this.engineName, ...pathParams }) - ); - }, engine: {}, }; +export const mockGenerateEnginePath = jest.fn((path, pathParams = {}) => + generatePath(path, { engineName: mockEngineValues.engineName, ...pathParams }) +); + jest.mock('../components/engine', () => ({ EngineLogic: { values: mockEngineValues }, + generateEnginePath: mockGenerateEnginePath, })); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.test.tsx index aea107a137da1..2cc6ff32d0ad9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.test.tsx @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { setMockValues } from '../../../__mocks__'; -import { mockEngineValues } from '../../__mocks__'; +import '../../__mocks__/engine_logic.mock'; import React from 'react'; import { shallow } from 'enzyme'; @@ -16,7 +15,6 @@ import { AnalyticsRouter } from './'; describe('AnalyticsRouter', () => { // Detailed route testing is better done via E2E tests it('renders', () => { - setMockValues(mockEngineValues); const wrapper = shallow(); expect(wrapper.find(Switch)).toHaveLength(1); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.tsx index 60c0f2a3fd3e8..f549a1a8d9091 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.tsx @@ -6,7 +6,6 @@ import React from 'react'; import { Route, Switch, Redirect } from 'react-router-dom'; -import { useValues } from 'kea'; import { APP_SEARCH_PLUGIN } from '../../../../../common/constants'; import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; @@ -22,7 +21,7 @@ import { ENGINE_ANALYTICS_QUERY_DETAILS_PATH, ENGINE_ANALYTICS_QUERY_DETAIL_PATH, } from '../../routes'; -import { EngineLogic } from '../engine'; +import { generateEnginePath } from '../engine'; import { ANALYTICS_TITLE, @@ -46,8 +45,6 @@ interface Props { engineBreadcrumb: BreadcrumbTrail; } export const AnalyticsRouter: React.FC = ({ engineBreadcrumb }) => { - const { generateEnginePath } = useValues(EngineLogic); - const ANALYTICS_BREADCRUMB = [...engineBreadcrumb, ANALYTICS_TITLE]; return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx index d8684355c1a81..bd4d088bc1d8a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { setMockValues, setMockActions } from '../../../__mocks__/kea.mock'; -import { mockEngineValues } from '../../__mocks__'; +import { setMockActions } from '../../../__mocks__/kea.mock'; +import '../../__mocks__/engine_logic.mock'; import React from 'react'; import { shallow } from 'enzyme'; @@ -21,7 +21,6 @@ describe('DocumentCreationButtons', () => { beforeEach(() => { jest.clearAllMocks(); - setMockValues(mockEngineValues); setMockActions(actions); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx index 93c93224b5982..3a53b3c83d9eb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { useActions, useValues } from 'kea'; +import { useActions } from 'kea'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -22,7 +22,7 @@ import { import { EuiCardTo } from '../../../shared/react_router_helpers'; import { DOCS_PREFIX, ENGINE_CRAWLER_PATH } from '../../routes'; -import { EngineLogic } from '../engine'; +import { generateEnginePath } from '../engine'; import { DocumentCreationLogic } from './'; @@ -33,7 +33,6 @@ interface Props { export const DocumentCreationButtons: React.FC = ({ disabled = false }) => { const { openDocumentCreation } = useActions(DocumentCreationLogic); - const { generateEnginePath } = useValues(EngineLogic); const crawlerLink = generateEnginePath(ENGINE_CRAWLER_PATH); return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.ts index b8d67ac56b3a2..8141ba73d418e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.ts @@ -12,7 +12,7 @@ import { KibanaLogic } from '../../../shared/kibana'; import { HttpLogic } from '../../../shared/http'; import { ENGINE_DOCUMENTS_PATH } from '../../routes'; -import { EngineLogic } from '../engine'; +import { EngineLogic, generateEnginePath } from '../engine'; import { FieldDetails } from './types'; @@ -52,7 +52,7 @@ export const DocumentDetailLogic = kea({ }), listeners: ({ actions }) => ({ getDocumentDetails: async ({ documentId }) => { - const { engineName, generateEnginePath } = EngineLogic.values; + const { engineName } = EngineLogic.values; const { navigateToUrl } = KibanaLogic.values; try { @@ -70,7 +70,7 @@ export const DocumentDetailLogic = kea({ } }, deleteDocument: async ({ documentId }) => { - const { engineName, generateEnginePath } = EngineLogic.values; + const { engineName } = EngineLogic.values; const { navigateToUrl } = KibanaLogic.values; const CONFIRM_DELETE = i18n.translate( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts index 32c3382cf187a..48cbaeef70c1a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts @@ -36,7 +36,6 @@ describe('EngineLogic', () => { dataLoading: true, engine: {}, engineName: '', - generateEnginePath: expect.any(Function), isMetaEngine: false, isSampleEngine: false, hasSchemaConflicts: false, @@ -198,28 +197,6 @@ describe('EngineLogic', () => { }); describe('selectors', () => { - describe('generateEnginePath', () => { - it('returns helper function that generates paths with engineName prefilled', () => { - mount({ engineName: 'hello-world' }); - - const generatedPath = EngineLogic.values.generateEnginePath('/engines/:engineName/example'); - expect(generatedPath).toEqual('/engines/hello-world/example'); - }); - - it('allows overriding engineName and filling other params', () => { - mount({ engineName: 'lorem-ipsum' }); - - const generatedPath = EngineLogic.values.generateEnginePath( - '/engines/:engineName/foo/:bar', - { - engineName: 'dolor-sit', - bar: 'baz', - } - ); - expect(generatedPath).toEqual('/engines/dolor-sit/foo/baz'); - }); - }); - describe('isSampleEngine', () => { it('should be set based on engine.sample', () => { const mockSampleEngine = { ...mockEngineData, sample: true }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts index 04d06b596080a..9f3fe721b74de 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts @@ -5,7 +5,6 @@ */ import { kea, MakeLogicType } from 'kea'; -import { generatePath } from 'react-router-dom'; import { HttpLogic } from '../../../shared/http'; @@ -16,7 +15,6 @@ interface EngineValues { dataLoading: boolean; engine: Partial; engineName: string; - generateEnginePath: Function; isMetaEngine: boolean; isSampleEngine: boolean; hasSchemaConflicts: boolean; @@ -78,15 +76,6 @@ export const EngineLogic = kea>({ ], }, selectors: ({ selectors }) => ({ - generateEnginePath: [ - () => [selectors.engineName], - (engineName) => { - const generateEnginePath = (path: string, pathParams: object = {}) => { - return generatePath(path, { engineName, ...pathParams }); - }; - return generateEnginePath; - }, - ], isMetaEngine: [() => [selectors.engine], (engine) => engine?.type === 'meta'], isSampleEngine: [() => [selectors.engine], (engine) => !!engine?.sample], hasSchemaConflicts: [ diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx index fd30e04d34932..0e5a7d56e9065 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx @@ -40,7 +40,7 @@ import { RESULT_SETTINGS_TITLE } from '../result_settings'; import { SEARCH_UI_TITLE } from '../search_ui'; import { API_LOGS_TITLE } from '../api_logs'; -import { EngineLogic } from './'; +import { EngineLogic, generateEnginePath } from './'; import { EngineDetails } from './types'; import './engine_nav.scss'; @@ -64,7 +64,6 @@ export const EngineNav: React.FC = () => { const { engineName, - generateEnginePath, dataLoading, isSampleEngine, isMetaEngine, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/index.ts index 4e7d81f73fb8d..7846eb9d03b71 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/index.ts @@ -7,3 +7,4 @@ export { EngineRouter } from './engine_router'; export { EngineNav } from './engine_nav'; export { EngineLogic } from './engine_logic'; +export { generateEnginePath } from './utils'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/utils.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/utils.test.ts new file mode 100644 index 0000000000000..cff4065c13f5e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/utils.test.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { mockEngineValues } from '../../__mocks__'; + +import { generateEnginePath } from './utils'; + +describe('generateEnginePath', () => { + mockEngineValues.engineName = 'hello-world'; + + it('generates paths with engineName filled from state', () => { + expect(generateEnginePath('/engines/:engineName/example')).toEqual( + '/engines/hello-world/example' + ); + }); + + it('allows overriding engineName and filling other params', () => { + expect( + generateEnginePath('/engines/:engineName/foo/:bar', { + engineName: 'override', + bar: 'baz', + }) + ).toEqual('/engines/override/foo/baz'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/utils.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/utils.ts new file mode 100644 index 0000000000000..b7efcbb6e6b27 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/utils.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { generatePath } from 'react-router-dom'; + +import { EngineLogic } from './'; + +/** + * Generate a path with engineName automatically filled from EngineLogic state + */ +export const generateEnginePath = (path: string, pathParams: object = {}) => { + const { engineName } = EngineLogic.values; + return generatePath(path, { engineName, ...pathParams }); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.test.tsx index 9da63ca639bbf..d7d22cafee432 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.test.tsx @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { setMockValues } from '../../../../__mocks__/kea.mock'; -import { mockEngineValues } from '../../../__mocks__'; +import '../../../__mocks__/engine_logic.mock'; import React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; @@ -19,7 +18,6 @@ describe('RecentApiLogs', () => { beforeAll(() => { jest.clearAllMocks(); - setMockValues(mockEngineValues); wrapper = shallow(); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.tsx index 19c931cefc1e3..207666ef67466 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.tsx @@ -5,7 +5,6 @@ */ import React from 'react'; -import { useValues } from 'kea'; import { EuiPageContent, @@ -17,14 +16,12 @@ import { import { EuiButtonTo } from '../../../../shared/react_router_helpers'; import { ENGINE_API_LOGS_PATH } from '../../../routes'; -import { EngineLogic } from '../../engine'; +import { generateEnginePath } from '../../engine'; import { RECENT_API_EVENTS } from '../../api_logs/constants'; import { VIEW_API_LOGS } from '../constants'; export const RecentApiLogs: React.FC = () => { - const { generateEnginePath } = useValues(EngineLogic); - return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx index 98718dea7130f..14fb19b8ca2be 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx @@ -5,7 +5,7 @@ */ import { setMockValues } from '../../../../__mocks__/kea.mock'; -import { mockEngineValues } from '../../../__mocks__'; +import '../../../__mocks__/engine_logic.mock'; import React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; @@ -21,7 +21,6 @@ describe('TotalCharts', () => { beforeAll(() => { jest.clearAllMocks(); setMockValues({ - ...mockEngineValues, startDate: '1970-01-01', queriesPerDay: [0, 1, 2, 3, 5, 10, 50], operationsPerDay: [0, 0, 0, 0, 0, 0, 0], diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx index 02453cc8a150f..e8454cdc95ebc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx @@ -20,7 +20,7 @@ import { import { EuiButtonTo } from '../../../../shared/react_router_helpers'; import { ENGINE_ANALYTICS_PATH, ENGINE_API_LOGS_PATH } from '../../../routes'; -import { EngineLogic } from '../../engine'; +import { generateEnginePath } from '../../engine'; import { TOTAL_QUERIES, TOTAL_API_OPERATIONS } from '../../analytics/constants'; import { VIEW_ANALYTICS, VIEW_API_LOGS, LAST_7_DAYS } from '../constants'; @@ -28,7 +28,6 @@ import { AnalyticsChart, convertToChartData } from '../../analytics'; import { EngineOverviewLogic } from '../'; export const TotalCharts: React.FC = () => { - const { generateEnginePath } = useValues(EngineLogic); const { startDate, queriesPerDay, operationsPerDay } = useValues(EngineOverviewLogic); return ( From 4281a347c6b3bb1304c8cf70ba82a34c466b2ae5 Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Thu, 21 Jan 2021 17:32:18 -0600 Subject: [PATCH 02/62] [Workplace Search] Add tests for remaining Sources components (#89026) * Remove history params We already replace the history.push functionality with KibanaLogic.values.navigateToUrl but the history object was still being passed around. * Add org sources container tests * Add tests for source router * Clean up leftover history imports * Add tests for SourcesRouter * Quick refactor for cleaner existence check Optional chaining FTW * Refactor to simplify setInterval logic This commit does a refactor to move the logic for polling for status to the logic file. In doing this I realized that we were intializing sources in the SourcesView, when we are actually already initializing sources in the components that use this, which are OrganizationSources and PrivateSources, the top-level containers. Because of this, I was able to remove the useEffect entireley, as the flash messages are cleared between page transitions in Kibana and the initialization of the sources ahppens in the containers. * Add tests for SourcesView * Fix type issue --- .../organization_sources.test.tsx | 63 +++++++++ .../content_sources/organization_sources.tsx | 3 +- .../views/content_sources/source_logic.ts | 4 +- .../content_sources/source_router.test.tsx | 120 ++++++++++++++++++ .../views/content_sources/source_router.tsx | 6 +- .../views/content_sources/sources_logic.ts | 28 +++- .../content_sources/sources_router.test.tsx | 60 +++++++++ .../content_sources/sources_view.test.tsx | 64 ++++++++++ .../views/content_sources/sources_view.tsx | 23 +--- 9 files changed, 337 insertions(+), 34 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_view.test.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.test.tsx new file mode 100644 index 0000000000000..1050150028aec --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.test.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import '../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../__mocks__'; + +import { shallow } from 'enzyme'; + +import React from 'react'; +import { Redirect } from 'react-router-dom'; + +import { contentSources } from '../../__mocks__/content_sources.mock'; + +import { Loading } from '../../../shared/loading'; +import { SourcesTable } from '../../components/shared/sources_table'; +import { ViewContentHeader } from '../../components/shared/view_content_header'; + +import { ADD_SOURCE_PATH, getSourcesPath } from '../../routes'; + +import { OrganizationSources } from './organization_sources'; + +describe('OrganizationSources', () => { + const initializeSources = jest.fn(); + const setSourceSearchability = jest.fn(); + + const mockValues = { + contentSources, + dataLoading: false, + }; + + beforeEach(() => { + setMockActions({ + initializeSources, + setSourceSearchability, + }); + setMockValues({ ...mockValues }); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(SourcesTable)).toHaveLength(1); + expect(wrapper.find(ViewContentHeader)).toHaveLength(1); + }); + + it('returns loading when loading', () => { + setMockValues({ ...mockValues, dataLoading: true }); + const wrapper = shallow(); + + expect(wrapper.find(Loading)).toHaveLength(1); + }); + + it('returns redirect when no sources', () => { + setMockValues({ ...mockValues, contentSources: [] }); + const wrapper = shallow(); + + expect(wrapper.find(Redirect).prop('to')).toEqual(getSourcesPath(ADD_SOURCE_PATH, true)); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.tsx index 880df3d086ccc..fdb536dd79771 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.tsx @@ -27,10 +27,11 @@ const ORG_HEADER_DESCRIPTION = 'Organization sources are available to the entire organization and can be assigned to specific user groups.'; export const OrganizationSources: React.FC = () => { - const { initializeSources, setSourceSearchability } = useActions(SourcesLogic); + const { initializeSources, setSourceSearchability, resetSourcesState } = useActions(SourcesLogic); useEffect(() => { initializeSources(); + return resetSourcesState; }, []); const { dataLoading, contentSources } = useValues(SourcesLogic); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts index fe958db9d0232..2de70009c56a2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts @@ -39,7 +39,7 @@ export interface SourceActions { ): { sourceId: string; source: { name: string } }; resetSourceState(): void; removeContentSource(sourceId: string): { sourceId: string }; - initializeSource(sourceId: string, history: object): { sourceId: string; history: object }; + initializeSource(sourceId: string): { sourceId: string }; getSourceConfigData(serviceType: string): { serviceType: string }; setButtonNotLoading(): void; } @@ -88,7 +88,7 @@ export const SourceLogic = kea>({ setSearchResults: (searchResultsResponse: SearchResultsResponse) => searchResultsResponse, setContentFilterValue: (contentFilterValue: string) => contentFilterValue, setActivePage: (activePage: number) => activePage, - initializeSource: (sourceId: string, history: object) => ({ sourceId, history }), + initializeSource: (sourceId: string) => ({ sourceId }), initializeFederatedSummary: (sourceId: string) => ({ sourceId }), searchContentSourceDocuments: (sourceId: string) => ({ sourceId }), updateContentSource: (sourceId: string, source: { name: string }) => ({ sourceId, source }), diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.test.tsx new file mode 100644 index 0000000000000..ac542f57b8fd4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.test.tsx @@ -0,0 +1,120 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import '../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../__mocks__'; + +import React from 'react'; +import { shallow } from 'enzyme'; +import { useParams } from 'react-router-dom'; + +import { Route, Switch } from 'react-router-dom'; + +import { contentSources } from '../../__mocks__/content_sources.mock'; + +import { SetWorkplaceSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; + +import { NAV } from '../../constants'; + +import { Loading } from '../../../shared/loading'; + +import { DisplaySettingsRouter } from './components/display_settings'; +import { Overview } from './components/overview'; +import { Schema } from './components/schema'; +import { SchemaChangeErrors } from './components/schema/schema_change_errors'; +import { SourceContent } from './components/source_content'; +import { SourceSettings } from './components/source_settings'; + +import { SourceRouter } from './source_router'; + +describe('SourceRouter', () => { + const initializeSource = jest.fn(); + const contentSource = contentSources[1]; + const customSource = contentSources[0]; + const mockValues = { + contentSource, + dataLoading: false, + }; + + beforeEach(() => { + setMockActions({ + initializeSource, + }); + setMockValues({ ...mockValues }); + (useParams as jest.Mock).mockImplementationOnce(() => ({ + sourceId: '1', + })); + }); + + it('returns Loading when loading', () => { + setMockValues({ ...mockValues, dataLoading: true }); + const wrapper = shallow(); + + expect(wrapper.find(Loading)).toHaveLength(1); + }); + + it('renders source routes (standard)', () => { + const wrapper = shallow(); + + expect(wrapper.find(Overview)).toHaveLength(1); + expect(wrapper.find(SourceSettings)).toHaveLength(1); + expect(wrapper.find(SourceContent)).toHaveLength(1); + expect(wrapper.find(Switch)).toHaveLength(1); + expect(wrapper.find(Route)).toHaveLength(3); + }); + + it('renders source routes (custom)', () => { + setMockValues({ ...mockValues, contentSource: customSource }); + const wrapper = shallow(); + + expect(wrapper.find(DisplaySettingsRouter)).toHaveLength(1); + expect(wrapper.find(Schema)).toHaveLength(1); + expect(wrapper.find(SchemaChangeErrors)).toHaveLength(1); + expect(wrapper.find(Route)).toHaveLength(6); + }); + + it('handles breadcrumbs while loading (standard)', () => { + setMockValues({ + ...mockValues, + contentSource: {}, + }); + + const loadingBreadcrumbs = ['Sources', '...']; + + const wrapper = shallow(); + + const overviewBreadCrumb = wrapper.find(SetPageChrome).at(0); + const contentBreadCrumb = wrapper.find(SetPageChrome).at(1); + const settingsBreadCrumb = wrapper.find(SetPageChrome).at(2); + + expect(overviewBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.OVERVIEW]); + expect(contentBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.CONTENT]); + expect(settingsBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.SETTINGS]); + }); + + it('handles breadcrumbs while loading (custom)', () => { + setMockValues({ + ...mockValues, + contentSource: { serviceType: 'custom' }, + }); + + const loadingBreadcrumbs = ['Sources', '...']; + + const wrapper = shallow(); + + const schemaBreadCrumb = wrapper.find(SetPageChrome).at(2); + const schemaErrorsBreadCrumb = wrapper.find(SetPageChrome).at(3); + const displaySettingsBreadCrumb = wrapper.find(SetPageChrome).at(4); + + expect(schemaBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.SCHEMA]); + expect(schemaErrorsBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.SCHEMA]); + expect(displaySettingsBreadCrumb.prop('trail')).toEqual([ + ...loadingBreadcrumbs, + NAV.DISPLAY_SETTINGS, + ]); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.tsx index 089ef0cd46a00..f46743778a168 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.tsx @@ -6,10 +6,9 @@ import React, { useEffect } from 'react'; -import { History } from 'history'; import { useActions, useValues } from 'kea'; import moment from 'moment'; -import { Route, Switch, useHistory, useParams } from 'react-router-dom'; +import { Route, Switch, useParams } from 'react-router-dom'; import { EuiButton, EuiCallOut, EuiHorizontalRule, EuiSpacer } from '@elastic/eui'; @@ -46,14 +45,13 @@ import { SourceInfoCard } from './components/source_info_card'; import { SourceSettings } from './components/source_settings'; export const SourceRouter: React.FC = () => { - const history = useHistory() as History; const { sourceId } = useParams() as { sourceId: string }; const { initializeSource } = useActions(SourceLogic); const { contentSource, dataLoading } = useValues(SourceLogic); const { isOrganization } = useValues(AppLogic); useEffect(() => { - initializeSource(sourceId, history); + initializeSource(sourceId); }, []); if (dataLoading) return ; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts index ab71f76484561..0a3d047796f49 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts @@ -77,6 +77,9 @@ interface ISourcesServerResponse { serviceTypes: Connector[]; } +let pollingInterval: number; +const POLLING_INTERVAL = 10000; + export const SourcesLogic = kea>({ path: ['enterprise_search', 'workplace_search', 'sources_logic'], actions: { @@ -169,6 +172,7 @@ export const SourcesLogic = kea>( try { const response = await HttpLogic.values.http.get(route); + actions.pollForSourceStatusChanges(); actions.onInitializeSources(response); } catch (e) { flashAPIErrors(e); @@ -181,18 +185,20 @@ export const SourcesLogic = kea>( } }, // We poll the server and if the status update, we trigger a new fetch of the sources. - pollForSourceStatusChanges: async () => { + pollForSourceStatusChanges: () => { const { isOrganization } = AppLogic.values; if (!isOrganization) return; const serverStatuses = values.serverStatuses; - const sourceStatuses = await fetchSourceStatuses(isOrganization); + pollingInterval = window.setInterval(async () => { + const sourceStatuses = await fetchSourceStatuses(isOrganization); - sourceStatuses.some((source: ContentSourceStatus) => { - if (serverStatuses && serverStatuses[source.id] !== source.status.status) { - return actions.initializeSources(); - } - }); + sourceStatuses.some((source: ContentSourceStatus) => { + if (serverStatuses && serverStatuses[source.id] !== source.status.status) { + return actions.initializeSources(); + } + }); + }, POLLING_INTERVAL); }, setSourceSearchability: async ({ sourceId, searchable }) => { const { isOrganization } = AppLogic.values; @@ -235,6 +241,14 @@ export const SourcesLogic = kea>( resetFlashMessages: () => { clearFlashMessages(); }, + resetSourcesState: () => { + clearInterval(pollingInterval); + }, + }), + events: () => ({ + beforeUnmount() { + clearInterval(pollingInterval); + }, }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.test.tsx new file mode 100644 index 0000000000000..7580203e759a9 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.test.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import '../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../__mocks__'; + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { Route, Switch, Redirect } from 'react-router-dom'; + +import { ADD_SOURCE_PATH, PERSONAL_SOURCES_PATH, SOURCES_PATH, getSourcesPath } from '../../routes'; + +import { SourcesRouter } from './sources_router'; + +describe('SourcesRouter', () => { + const resetSourcesState = jest.fn(); + const mockValues = { + account: { canCreatePersonalSources: true }, + isOrganization: true, + hasPlatinumLicense: true, + }; + + beforeEach(() => { + setMockActions({ + resetSourcesState, + }); + setMockValues({ ...mockValues }); + }); + + it('renders sources routes', () => { + const TOTAL_ROUTES = 62; + const wrapper = shallow(); + + expect(wrapper.find(Switch)).toHaveLength(1); + expect(wrapper.find(Route)).toHaveLength(TOTAL_ROUTES); + }); + + it('redirects when nonplatinum license and accountOnly context', () => { + setMockValues({ ...mockValues, hasPlatinumLicense: false }); + const wrapper = shallow(); + + expect(wrapper.find(Redirect).first().prop('from')).toEqual(ADD_SOURCE_PATH); + expect(wrapper.find(Redirect).first().prop('to')).toEqual(SOURCES_PATH); + }); + + it('redirects when cannot create sources', () => { + setMockValues({ ...mockValues, account: { canCreatePersonalSources: false } }); + const wrapper = shallow(); + + expect(wrapper.find(Redirect).last().prop('from')).toEqual( + getSourcesPath(ADD_SOURCE_PATH, false) + ); + expect(wrapper.find(Redirect).last().prop('to')).toEqual(PERSONAL_SOURCES_PATH); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_view.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_view.test.tsx new file mode 100644 index 0000000000000..7deb87f4311a5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_view.test.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import '../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../__mocks__'; + +import { shallow } from 'enzyme'; + +import React from 'react'; + +import { EuiModal } from '@elastic/eui'; + +import { Loading } from '../../../shared/loading'; + +import { SourcesView } from './sources_view'; + +describe('SourcesView', () => { + const resetPermissionsModal = jest.fn(); + const permissionsModal = { + addedSourceName: 'mySource', + serviceType: 'jira', + additionalConfiguration: true, + }; + + const mockValues = { + permissionsModal, + dataLoading: false, + }; + + const children =

test

; + + beforeEach(() => { + setMockActions({ + resetPermissionsModal, + }); + setMockValues({ ...mockValues }); + }); + + it('renders', () => { + const wrapper = shallow({children}); + + expect(wrapper.find('PermissionsModal')).toHaveLength(1); + expect(wrapper.find('[data-test-subj="TestChildren"]')).toHaveLength(1); + }); + + it('returns loading when loading', () => { + setMockValues({ ...mockValues, dataLoading: true }); + const wrapper = shallow({children}); + + expect(wrapper.find(Loading)).toHaveLength(1); + }); + + it('calls function on modal close', () => { + const wrapper = shallow({children}); + const modal = wrapper.find('PermissionsModal').dive().find(EuiModal); + modal.prop('onClose')(); + + expect(resetPermissionsModal).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_view.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_view.tsx index 7e3c14b203e9e..9e6c8f5b7319e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_view.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_view.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useEffect } from 'react'; +import React from 'react'; import { useActions, useValues } from 'kea'; @@ -22,8 +22,6 @@ import { EuiText, } from '@elastic/eui'; -import { clearFlashMessages } from '../../../shared/flash_messages'; - import { Loading } from '../../../shared/loading'; import { SourceIcon } from '../../components/shared/source_icon'; @@ -31,29 +29,14 @@ import { EXTERNAL_IDENTITIES_DOCS_URL, DOCUMENT_PERMISSIONS_DOCS_URL } from '../ import { SourcesLogic } from './sources_logic'; -const POLLING_INTERVAL = 10000; - interface SourcesViewProps { children: React.ReactNode; } export const SourcesView: React.FC = ({ children }) => { - const { initializeSources, pollForSourceStatusChanges, resetPermissionsModal } = useActions( - SourcesLogic - ); - + const { resetPermissionsModal } = useActions(SourcesLogic); const { dataLoading, permissionsModal } = useValues(SourcesLogic); - useEffect(() => { - initializeSources(); - const pollingInterval = window.setInterval(pollForSourceStatusChanges, POLLING_INTERVAL); - - return () => { - clearFlashMessages(); - clearInterval(pollingInterval); - }; - }, []); - if (dataLoading) return ; const PermissionsModal = ({ @@ -113,7 +96,7 @@ export const SourcesView: React.FC = ({ children }) => { return ( <> - {!!permissionsModal && permissionsModal.additionalConfiguration && ( + {permissionsModal?.additionalConfiguration && ( Date: Thu, 21 Jan 2021 17:00:24 -0700 Subject: [PATCH 03/62] [Docs] Add geo threshold and containment docs (#88783) Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> --- docs/user/alerting/alert-types.asciidoc | 2 +- docs/user/alerting/geo-alert-types.asciidoc | 127 ++++++++++++++++++ ...es-tracking-containment-action-options.png | Bin 0 -> 19963 bytes ...-types-tracking-containment-conditions.png | Bin 0 -> 22187 bytes .../images/alert-types-tracking-select.png | Bin 0 -> 37690 bytes ...rt-types-tracking-threshold-conditions.png | Bin 0 -> 37636 bytes docs/user/alerting/index.asciidoc | 1 + 7 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 docs/user/alerting/geo-alert-types.asciidoc create mode 100644 docs/user/alerting/images/alert-types-tracking-containment-action-options.png create mode 100644 docs/user/alerting/images/alert-types-tracking-containment-conditions.png create mode 100644 docs/user/alerting/images/alert-types-tracking-select.png create mode 100644 docs/user/alerting/images/alert-types-tracking-threshold-conditions.png diff --git a/docs/user/alerting/alert-types.asciidoc b/docs/user/alerting/alert-types.asciidoc index 7de5ff56228cc..7c5a957d1cf79 100644 --- a/docs/user/alerting/alert-types.asciidoc +++ b/docs/user/alerting/alert-types.asciidoc @@ -1,6 +1,6 @@ [role="xpack"] [[alert-types]] -== Alert types +== Standard stack alert types {kib} supplies alert types in two ways: some are built into {kib} (these are known as stack alerts), while domain-specific alert types are registered by {kib} apps such as <>, <>, and <>. diff --git a/docs/user/alerting/geo-alert-types.asciidoc b/docs/user/alerting/geo-alert-types.asciidoc new file mode 100644 index 0000000000000..c04cf4bca4320 --- /dev/null +++ b/docs/user/alerting/geo-alert-types.asciidoc @@ -0,0 +1,127 @@ +[role="xpack"] +[[geo-alert-types]] +== Geo alert types + +experimental[] Two additional stack alerts are available: +<> and <>. To enable, +add the following configuration to your `kibana.yml`: + +```yml +xpack.stack_alerts.enableGeoAlerting: true +``` + +As with other stack alerts, you need `all` access to the *Stack Alerts* feature +to be able to create and edit either of the geo alerts. +See <> for more information on configuring roles that provide access to this feature. + +[float] +=== Geo alert requirements + +To create either a *Tracking threshold* or a *Tracking containment* alert, the +following requirements must be present: + +- *Tracks index or index pattern*: An index containing a `geo_point` field, `date` field, +and some form of entity identifier. An entity identifier is a `keyword` or `number` +field that consistently identifies the entity to be tracked. The data in this index should be dynamically +updating so that there are entity movements to alert upon. +- *Boundaries index or index pattern*: An index containing `geo_shape` data, such as boundary data and bounding box data. +This data is presumed to be static (not updating). Shape data matching the query is +harvested once when the alert is created and anytime after when the alert is re-enabled +after disablement. + +By design, current interval entity locations (_current_ is determined by `date` in +the *Tracked index or index pattern*) are queried to determine if they are contained +within any monitored boundaries. Entity +data should be somewhat "real time", meaning the dates of new documents aren’t older +than the current time minus the amount of the interval. If data older than +`now - ` is ingested, it won't trigger an alert. + +[float] +=== Creating a geo alert +Both *threshold* and *containment* alerts can be created by clicking the *Create* +button in the <>. +Complete the <>. +Select <> to generate an alert when an entity crosses a boundary, and you desire the +ability to highlight lines of crossing on a custom map. +Select +<> if an entity should send out constant alerts +while contained within a boundary (this feature is optional) or if the alert is generally +just more focused around activity when an entity exists within a shape. + +[role="screenshot"] +image::images/alert-types-tracking-select.png[Choosing a tracking alert type] + +[NOTE] +================================================== +With recent advances in the alerting framework, most of the features +available in Tracking threshold alerts can be replicated with just +a little more work in Tracking containment alerts. The capabilities of Tracking +threshold alerts may be deprecated or folded into Tracking containment alerts +in the future. +================================================== + +[float] +[[alert-type-tracking-threshold]] +=== Tracking threshold +The Tracking threshold alert type runs an {es} query over indices, comparing the latest +entity locations with their previous locations. In the event that an entity has crossed a +boundary from the selected boundary index, an alert may be generated. + +[float] +==== Defining the conditions +Tracking threshold has a *Delayed evaluation offset* and 4 clauses that define the +condition to detect, as well as 2 Kuery bars used to provide additional filtering +context for each of the indices. + +[role="screenshot"] +image::images/alert-types-tracking-threshold-conditions.png[Five clauses define the condition to detect] + + +Delayed evaluation offset:: If a data source lags or is intermittent, you may supply +an optional value to evaluate alert conditions following a fixed delay. For instance, if data +is consistently indexed 5-10 minutes following its original timestamp, a *Delayed evaluation +offset* of `10 minutes` would ensure that alertable instances are still captured. +Index (entity):: This clause requires an *index or index pattern*, a *time field* that will be used for the *time window*, and a *`geo_point` field* for tracking. +By:: This clause specifies the field to use in the previously provided +*index or index pattern* for tracking Entities. An entity is a `keyword` +or `number` field that consistently identifies the entity to be tracked. +When entity:: This clause specifies which crossing option to track. The values +*Entered*, *Exited*, and *Crossed* can be selected to indicate which crossing conditions +should trigger an alert. *Entered* alerts on entry into a boundary, *Exited* alerts on exit +from a boundary, and *Crossed* alerts on all boundary crossings whether they be entrances +or exits. +Index (Boundary):: This clause requires an *index or index pattern*, a *`geo_shape` field* +identifying boundaries, and an optional *Human-readable boundary name* for better alerting +messages. + +[float] +[[alert-type-tracking-containment]] +=== Tracking containment +The Tracking containment alert type runs an {es} query over indices, determining if any +documents are currently contained within any boundaries from the specified boundary index. +In the event that an entity is contained within a boundary, an alert may be generated. + +[float] +==== Defining the conditions +Tracking containment alerts have 3 clauses that define the condition to detect, +as well as 2 Kuery bars used to provide additional filtering context for each of the indices. + +[role="screenshot"] +image::images/alert-types-tracking-containment-conditions.png[Five clauses define the condition to detect] + +Index (entity):: This clause requires an *index or index pattern*, a *time field* that will be used for the *time window*, and a *`geo_point` field* for tracking. +When entity:: This clause specifies which crossing option to track. The values +*Entered*, *Exited*, and *Crossed* can be selected to indicate which crossing conditions +should trigger an alert. *Entered* alerts on entry into a boundary, *Exited* alerts on exit +from a boundary, and *Crossed* alerts on all boundary crossings whether they be entrances +or exits. +Index (Boundary):: This clause requires an *index or index pattern*, a *`geo_shape` field* +identifying boundaries, and an optional *Human-readable boundary name* for better alerting +messages. + +Conditions for how an alert is tracked can be specified uniquely for each individual action. +An alert can be triggered either when a containment condition is met or when an entity +is no longer contained. + +[role="screenshot"] +image::images/alert-types-tracking-containment-action-options.png[Five clauses define the condition to detect] diff --git a/docs/user/alerting/images/alert-types-tracking-containment-action-options.png b/docs/user/alerting/images/alert-types-tracking-containment-action-options.png new file mode 100644 index 0000000000000000000000000000000000000000..c0a045f8273829d7a9fbb0d07b19c2a137dbc46f GIT binary patch literal 19963 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 JPDHLkV1n`+-|qkb literal 0 HcmV?d00001 diff --git a/docs/user/alerting/images/alert-types-tracking-containment-conditions.png b/docs/user/alerting/images/alert-types-tracking-containment-conditions.png new file mode 100644 index 0000000000000000000000000000000000000000..32c17d2245d23c9d526e252b802deb75c6984931 GIT binary patch literal 22187 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

Z=b$?CE%Gp~U{yd9HNKQ^@G22Il7oCaCoLa&z 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 Date: Thu, 21 Jan 2021 18:27:27 -0700 Subject: [PATCH 04/62] [Maps] fix setting "apply global time" switch not working with blended vector layer (#88996) * [Maps] fix setting "apply global time" switch not working with blended vector layer * review feedback Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../blended_vector_layer.test.tsx | 46 +++++++++++-------- .../blended_vector_layer.ts | 1 + .../maps/public/classes/layers/layer.tsx | 5 +- .../connected_components/mb_map/mb_map.tsx | 2 +- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.test.tsx b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.test.tsx index 1321593f015c0..e029480bd8616 100644 --- a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.test.tsx +++ b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.test.tsx @@ -7,7 +7,10 @@ import { SCALING_TYPES, SOURCE_TYPES } from '../../../../common/constants'; import { BlendedVectorLayer } from './blended_vector_layer'; import { ESSearchSource } from '../../sources/es_search_source'; -import { ESGeoGridSourceDescriptor } from '../../../../common/descriptor_types'; +import { + AbstractESSourceDescriptor, + ESGeoGridSourceDescriptor, +} from '../../../../common/descriptor_types'; jest.mock('../../../kibana_services', () => { return { @@ -53,27 +56,12 @@ describe('getSource', () => { expect(source.cloneDescriptor().type).toBe(SOURCE_TYPES.ES_GEO_GRID); }); - test('cluster source applyGlobalQuery should be true when document source applyGlobalQuery is true', async () => { - const blendedVectorLayer = new BlendedVectorLayer({ - source: new ESSearchSource(documentSourceDescriptor), - layerDescriptor: BlendedVectorLayer.createDescriptor( - { - sourceDescriptor: documentSourceDescriptor, - __dataRequests: [clusteredDataRequest], - }, - mapColors - ), - }); - - const source = blendedVectorLayer.getSource(); - expect((source.cloneDescriptor() as ESGeoGridSourceDescriptor).applyGlobalQuery).toBe(true); - }); - - test('cluster source applyGlobalQuery should be false when document source applyGlobalQuery is false', async () => { + test('cluster source AbstractESSourceDescriptor properties should mirror document source AbstractESSourceDescriptor properties', async () => { const blendedVectorLayer = new BlendedVectorLayer({ source: new ESSearchSource({ ...documentSourceDescriptor, applyGlobalQuery: false, + applyGlobalTime: false, }), layerDescriptor: BlendedVectorLayer.createDescriptor( { @@ -85,7 +73,27 @@ describe('getSource', () => { }); const source = blendedVectorLayer.getSource(); - expect((source.cloneDescriptor() as ESGeoGridSourceDescriptor).applyGlobalQuery).toBe(false); + const sourceDescriptor = source.cloneDescriptor() as ESGeoGridSourceDescriptor; + const abstractEsSourceDescriptor: AbstractESSourceDescriptor = { + // Purposely grabbing properties instead of using spread operator + // to ensure type check will fail when new properties are added to AbstractESSourceDescriptor. + // In the event of type check failure, ensure test is updated with new property and that new property + // is correctly passed to clustered source descriptor. + type: sourceDescriptor.type, + id: sourceDescriptor.id, + indexPatternId: sourceDescriptor.indexPatternId, + geoField: sourceDescriptor.geoField, + applyGlobalQuery: sourceDescriptor.applyGlobalQuery, + applyGlobalTime: sourceDescriptor.applyGlobalTime, + }; + expect(abstractEsSourceDescriptor).toEqual({ + type: sourceDescriptor.type, + id: sourceDescriptor.id, + geoField: 'myGeoField', + indexPatternId: 'myIndexPattern', + applyGlobalQuery: false, + applyGlobalTime: false, + } as AbstractESSourceDescriptor); }); }); diff --git a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts index 825f6ed74777a..5b33738a91a28 100644 --- a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts @@ -62,6 +62,7 @@ function getClusterSource(documentSource: IESSource, documentStyle: IVectorStyle requestType: RENDER_AS.POINT, }); clusterSourceDescriptor.applyGlobalQuery = documentSource.getApplyGlobalQuery(); + clusterSourceDescriptor.applyGlobalTime = documentSource.getApplyGlobalTime(); clusterSourceDescriptor.metrics = [ { type: AGG_TYPE.COUNT, diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index 060ff4d46fa2a..fe13e4f0ac2f6 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -5,6 +5,7 @@ */ /* eslint-disable @typescript-eslint/consistent-type-definitions */ +import { Map as MbMap } from 'mapbox-gl'; import { Query } from 'src/plugins/data/public'; import _ from 'lodash'; import React, { ReactElement } from 'react'; @@ -68,7 +69,7 @@ export interface ILayer { ownsMbLayerId(mbLayerId: string): boolean; ownsMbSourceId(mbSourceId: string): boolean; canShowTooltip(): boolean; - syncLayerWithMB(mbMap: unknown): void; + syncLayerWithMB(mbMap: MbMap): void; getLayerTypeIconName(): string; isDataLoaded(): boolean; getIndexPatternIds(): string[]; @@ -418,7 +419,7 @@ export class AbstractLayer implements ILayer { return false; } - syncLayerWithMB(mbMap: unknown) { + syncLayerWithMB(mbMap: MbMap) { throw new Error('Should implement AbstractLayer#syncLayerWithMB'); } diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx index 4dc765f1704a0..820453f166a46 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx @@ -332,7 +332,7 @@ export class MBMap extends Component { this.props.layerList, this.props.spatialFiltersLayer ); - this.props.layerList.forEach((layer) => layer.syncLayerWithMB(this.state.mbMap)); + this.props.layerList.forEach((layer) => layer.syncLayerWithMB(this.state.mbMap!)); syncLayerOrder(this.state.mbMap, this.props.spatialFiltersLayer, this.props.layerList); }; From a0bfdf87fd51cad4973a6d07aca66a90c98b2ed3 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 21 Jan 2021 19:35:40 -0700 Subject: [PATCH 05/62] [Maps] fix Filter shape stops showing feedback when data refreshes (#89009) * [Maps] fix Filter shape stops showing feedback when data refreshes * update comment * add curly braces around if --- .../mb_map/sort_layers.test.ts | 2 ++ .../connected_components/mb_map/sort_layers.ts | 16 ++++++++++++++++ .../public/connected_components/mb_map/utils.js | 3 ++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.test.ts b/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.test.ts index 9e85c7b04b266..4e9cb499cf704 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.test.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.test.ts @@ -135,6 +135,7 @@ describe('sortLayer', () => { { id: `${BRAVO_LAYER_ID}_circle`, type: 'circle' } as MbLayer, { id: `${SPATIAL_FILTERS_LAYER_ID}_fill`, type: 'fill' } as MbLayer, { id: `${SPATIAL_FILTERS_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + { id: `gl-draw-polygon-fill-active.cold`, type: 'fill' } as MbLayer, { id: `${CHARLIE_LAYER_ID}_text`, type: 'symbol', @@ -158,6 +159,7 @@ describe('sortLayer', () => { 'alpha_text', 'alpha_circle', 'charlie_text', + 'gl-draw-polygon-fill-active.cold', 'SPATIAL_FILTERS_LAYER_ID_fill', 'SPATIAL_FILTERS_LAYER_ID_circle', ]); diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.ts b/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.ts index dda43269e32d8..adf68ffb310bc 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.ts @@ -28,6 +28,10 @@ export function getIsTextLayer(mbLayer: MbLayer) { }); } +export function isGlDrawLayer(mbLayerId: string) { + return mbLayerId.startsWith('gl-draw'); +} + function doesMbLayerBelongToMapLayerAndClass( mapLayer: ILayer, mbLayer: MbLayer, @@ -118,6 +122,18 @@ export function syncLayerOrder(mbMap: MbMap, spatialFiltersLayer: ILayer, layerL } let beneathMbLayerId = getBottomMbLayerId(mbLayers, spatialFiltersLayer, LAYER_CLASS.ANY); + // Ensure gl-draw layers are on top of all layerList layers + const glDrawLayer = ({ + ownsMbLayerId: (mbLayerId: string) => { + return isGlDrawLayer(mbLayerId); + }, + } as unknown) as ILayer; + moveMapLayer(mbMap, mbLayers, glDrawLayer, LAYER_CLASS.ANY, beneathMbLayerId); + const glDrawBottomMbLayerId = getBottomMbLayerId(mbLayers, glDrawLayer, LAYER_CLASS.ANY); + if (glDrawBottomMbLayerId) { + beneathMbLayerId = glDrawBottomMbLayerId; + } + // Sort map layer labels [...layerList] .reverse() diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/utils.js b/x-pack/plugins/maps/public/connected_components/mb_map/utils.js index f12f34061756f..2f8852174c29e 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/utils.js +++ b/x-pack/plugins/maps/public/connected_components/mb_map/utils.js @@ -5,6 +5,7 @@ */ import { RGBAImage } from './image_utils'; +import { isGlDrawLayer } from './sort_layers'; export function removeOrphanedSourcesAndLayers(mbMap, layerList, spatialFilterLayer) { const mbStyle = mbMap.getStyle(); @@ -17,7 +18,7 @@ export function removeOrphanedSourcesAndLayers(mbMap, layerList, spatialFilterLa } // ignore gl-draw layers - if (mbLayer.id.startsWith('gl-draw')) { + if (isGlDrawLayer(mbLayer.id)) { return; } From 58c54b6f8583eea111f5a039c422bcb17de681f6 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Fri, 22 Jan 2021 09:02:42 -0500 Subject: [PATCH 06/62] Add a high level overview of the Kibana platform and plugin development. (#87560) * add platform intro * address code review comments (wip) * incorporate more information about plugins * put back Josh's suggestion * Update dev_docs/kibana_platform_plugin_intro.mdx Co-authored-by: Brandon Kobel * try another angle * further refinements * sp * Update kibana_platform_plugin_intro.mdx Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Brandon Kobel --- .../kibana_platform_plugin_end_user.png | Bin 0 -> 355010 bytes dev_docs/assets/platform_plugin_cycle.png | Bin 0 -> 202395 bytes dev_docs/kibana_platform_plugin_intro.mdx | 305 ++++++++++++++++++ 3 files changed, 305 insertions(+) create mode 100644 dev_docs/assets/kibana_platform_plugin_end_user.png create mode 100644 dev_docs/assets/platform_plugin_cycle.png create mode 100644 dev_docs/kibana_platform_plugin_intro.mdx diff --git a/dev_docs/assets/kibana_platform_plugin_end_user.png b/dev_docs/assets/kibana_platform_plugin_end_user.png new file mode 100644 index 0000000000000000000000000000000000000000..a0e32a35ffe6054cc1c17341eb8052d3c901b92b GIT binary patch literal 355010 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%0DqU|vl=DQohkO&o$aw2QW3|*pSvUX!faEH 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 literal 0 HcmV?d00001 diff --git a/dev_docs/assets/platform_plugin_cycle.png b/dev_docs/assets/platform_plugin_cycle.png new file mode 100644 index 0000000000000000000000000000000000000000..533a38c4ed9c4db71b94c00a86c2aad026fd9aa6 GIT binary patch literal 202395 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%0D1lN=X?k?Bw1Z9_x30+ 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%0A$c=m8eAa1%T_TEM{JGJxKo06rZ4rdGQer@$1 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±a2W&NG>}5gMVrm`i{MFil6ENK1y~tibDBSWy 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 provides basic utilities to search, query, and filter data in Elasticsearch. +This code is not part of Core, but is still fundamental for building a plugin, + and we strongly encourage using this service over querying Elasticsearch directly. + + +We currently have three kinds of public services: + + - platform services provided by `core` + - platform services provided by plugins, that can, and should, be used by every plugin (e.g. ) . + - shared services provided by plugins, that are only relevant for only a few, specific plugins (e.g. "presentation utils"). + +Two common questions we encounter are: + +1. Which services are platform services? +2. What is the difference between platform code supplied by core, and platform code supplied by plugins? + +We don't have great answers to those questions today. Currently, the best answers we have are: + +1. Platform plugins are _usually_ plugins that are managed by the Platform Group, but we are starting to see some exceptions. +2. `core` code contains the most fundamental and stable services needed for plugin development. Everything else goes in a plugin. + +We will continue to focus on adding clarity around these types of services and what developers can expect from each. + + + + + +When the Kibana platform and plugin infrastructure was built, we thought of two types of code: core services, and other plugin services. We planned to keep the most stable and fundamental +code needed to build plugins inside core. + +In reality, we ended up with many platform-like services living outside of core, with no (short term) intention of moving them. We highly encourage plugin developers to use +them, so we consider them part of platform services. + +When we built our platform system, we also thought we'd end up with only a handful of large plugins outside core. Users could turn certain plugins off, to minimize the code + footprint and speed up Kibana. + +In reality, our plugin model ended up being used like micro-services. Plugins are the only form of encapsulation we provide developers, and they liked it! However, we ended + up with a ton of small plugins, that developers never intended to be uninstallable, nor tested in this manner. We are considering ways to provide developers the ability to build services + with the encapsulation + they desire, without the need to build a plugin. + +Another side effect of having many small plugins is that common code often ends up extracted into another plugin. Use case specific utilities are exported, + that are not meant to be used in a general manner. This makes our definition of "platform code" a bit trickier to define. We'd like to say "The platform is made up of + every publically exposed service", but in today's world, that wouldn't be a very accurate picture. + +We recognize the need to better clarify the relationship between core functionality, platform-like plugin functionality, and functionality exposed by other plugins. + It's something we will be working on! + + + +The main difference between core functionality and functionality supplied by plugins, is in how it is accessed. Core is +passed to plugins as the first parameter to their `start` and `setup` lifecycle functions, while plugin supplied functionality is passed as the +second parameter. Plugin dependencies must be declared explicitly inside the `kibana.json` file. Core functionality is always provided. Read the +section on [how plugins interact with eachother and core](#how-plugins-interact-with-each-other-and-core) for more information. + +## The anatomy of a plugin + +Plugins are defined as classes and present themselves to Kibana through a simple wrapper function. A plugin can have browser-side code, server-side code, +or both. There is no architectural difference between a plugin in the browser and a plugin on the server. In both places, you describe your plugin similarly, +and you interact with Core and other plugins in the same way. + +The basic file structure of a Kibana plugin named demo that has both client-side and server-side code would be: + +``` +plugins/ + demo + kibana.json [1] + public + index.ts [2] + plugin.ts [3] + server + index.ts [4] + plugin.ts [5] +``` + +### [1] kibana.json + +`kibana.json` is a static manifest file that is used to identify the plugin and to specify if this plugin has server-side code, browser-side code, or both: + +``` +{ + "id": "demo", + "version": "kibana", + "server": true, + "ui": true +} +``` + +### [2] public/index.ts + +`public/index.ts` is the entry point into the client-side code of this plugin. It must export a function named plugin, which will receive a standard set of + core capabilities as an argument. It should return an instance of its plugin class for Kibana to load. + +``` +import type { PluginInitializerContext } from 'kibana/server'; +import { DemoPlugin } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new DemoPlugin(initializerContext); +} +``` + +### [3] public/plugin.ts + +`public/plugin.ts` is the client-side plugin definition itself. Technically speaking, it does not need to be a class or even a separate file from the entry + point, but all plugins at Elastic should be consistent in this way. + + + ```ts +import type { Plugin, PluginInitializerContext, CoreSetup, CoreStart } from 'kibana/server'; + +export class DemoPlugin implements Plugin { + constructor(initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup) { + // called when plugin is setting up during Kibana's startup sequence + } + + public start(core: CoreStart) { + // called after all plugins are set up + } + + public stop() { + // called when plugin is torn down during Kibana's shutdown sequence + } +} + ``` + + +### [4] server/index.ts + +`server/index.ts` is the entry-point into the server-side code of this plugin. It is identical in almost every way to the client-side entry-point: + +### [5] server/plugin.ts + +`server/plugin.ts` is the server-side plugin definition. The shape of this plugin is the same as it’s client-side counter-part: + +```ts +import type { Plugin, PluginInitializerContext, CoreSetup, CoreStart } from 'kibana/server'; + +export class DemoPlugin implements Plugin { + constructor(initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup) { + // called when plugin is setting up during Kibana's startup sequence + } + + public start(core: CoreStart) { + // called after all plugins are set up + } + + public stop() { + // called when plugin is torn down during Kibana's shutdown sequence + } +} +``` + +Kibana does not impose any technical restrictions on how the the internals of a plugin are architected, though there are certain +considerations related to how plugins integrate with core APIs and APIs exposed by other plugins that may greatly impact how they are built. + +## Plugin lifecycles & Core services + +The various independent domains that make up core are represented by a series of services. Those services expose public interfaces that are provided to all plugins. +Services expose different features at different parts of their lifecycle. We describe the lifecycle of core services and plugins with specifically-named functions on the service definition. + +Kibana has three lifecycles: setup, start, and stop. Each plugin’s setup function is called sequentially while Kibana is setting up on the server or when it is being loaded in the browser. The start functions are called sequentially after setup has been completed for all plugins. The stop functions are called sequentially while Kibana is gracefully shutting down the server or when the browser tab or window is being closed. + +The table below explains how each lifecycle relates to the state of Kibana. + +| lifecycle | purpose | server | browser | +| ---------- | ------ | ------- | ----- | +| setup | perform "registration" work to setup environment for runtime |configure REST API endpoint, register saved object types, etc. | configure application routes in SPA, register custom UI elements in extension points, etc. | +| start | bootstrap runtime logic | respond to an incoming request, request Elasticsearch server, etc. | start polling Kibana server, update DOM tree in response to user interactions, etc.| +| stop | cleanup runtime | dispose of active handles before the server shutdown. | store session data in the LocalStorage when the user navigates away from Kibana, etc. | + +Different service interfaces can and will be passed to setup, start, and stop because certain functionality makes sense in the context of a running plugin while other types +of functionality may have restrictions or may only make sense in the context of a plugin that is stopping. + +## How plugin's interact with each other, and Core + +The lifecycle-specific contracts exposed by core services are always passed as the first argument to the equivalent lifecycle function in a plugin. +For example, the core http service exposes a function createRouter to all plugin setup functions. To use this function to register an HTTP route handler, +a plugin just accesses it off of the first argument: + +```ts +import type { CoreSetup } from 'kibana/server'; + +export class DemoPlugin { + public setup(core: CoreSetup) { + const router = core.http.createRouter(); + // handler is called when '/path' resource is requested with `GET` method + router.get({ path: '/path', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); + } +} +``` + +Unlike core, capabilities exposed by plugins are not automatically injected into all plugins. +Instead, if a plugin wishes to use the public interface provided by another plugin, it must first declare that plugin as a + dependency in it’s kibana.json manifest file. + +** foobar plugin.ts: ** + +``` +import type { Plugin } from 'kibana/server'; +export interface FoobarPluginSetup { [1] + getFoo(): string; +} + +export interface FoobarPluginStart { [1] + getBar(): string; +} + +export class MyPlugin implements Plugin { + public setup(): FoobarPluginSetup { + return { + getFoo() { + return 'foo'; + }, + }; + } + + public start(): FoobarPluginStart { + return { + getBar() { + return 'bar'; + }, + }; + } +} +``` +[1] We highly encourage plugin authors to explicitly declare public interfaces for their plugins. + + +** demo kibana.json** + +``` +{ + "id": "demo", + "requiredPlugins": ["foobar"], + "server": true, + "ui": true +} +``` + +With that specified in the plugin manifest, the appropriate interfaces are then available via the second argument of setup and/or start: + +```ts +import type { CoreSetup, CoreStart } from 'kibana/server'; +import type { FoobarPluginSetup, FoobarPluginStart } from '../../foobar/server'; + +interface DemoSetupPlugins { [1] + foobar: FoobarPluginSetup; +} + +interface DemoStartPlugins { + foobar: FoobarPluginStart; +} + +export class DemoPlugin { + public setup(core: CoreSetup, plugins: DemoSetupPlugins) { [2] + const { foobar } = plugins; + foobar.getFoo(); // 'foo' + foobar.getBar(); // throws because getBar does not exist + } + + public start(core: CoreStart, plugins: DemoStartPlugins) { [3] + const { foobar } = plugins; + foobar.getFoo(); // throws because getFoo does not exist + foobar.getBar(); // 'bar' + } + + public stop() {} +} +``` + +[1] The interface for plugin’s dependencies must be manually composed. You can do this by importing the appropriate type from the plugin and constructing an interface where the property name is the plugin’s ID. + +[2] These manually constructed types should then be used to specify the type of the second argument to the plugin. + +[3] Notice that the type for the setup and start lifecycles are different. Plugin lifecycle functions can only access the APIs that are exposed during that lifecycle. From 49d95f6fb1678af9db7243bd5b5026d9dad47adb Mon Sep 17 00:00:00 2001 From: Kevin Logan <56395104+kevinlog@users.noreply.github.com> Date: Fri, 22 Jan 2021 12:12:59 -0500 Subject: [PATCH 07/62] [Fleet] Add `updateFleetRoleIfExists()` in order to update `fleet_enroll` permissions if role already exists (#88000) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/fleet/server/services/setup.ts | 41 ++++-- .../fleet_api_integration/apis/fleet_setup.ts | 124 ++++++++++++++++++ .../test/fleet_api_integration/apis/index.js | 2 + 3 files changed, 158 insertions(+), 9 deletions(-) create mode 100644 x-pack/test/fleet_api_integration/apis/fleet_setup.ts diff --git a/x-pack/plugins/fleet/server/services/setup.ts b/x-pack/plugins/fleet/server/services/setup.ts index 1ce7b1d85c8e4..0dcdfeb7b3801 100644 --- a/x-pack/plugins/fleet/server/services/setup.ts +++ b/x-pack/plugins/fleet/server/services/setup.ts @@ -59,6 +59,7 @@ async function createSetupSideEffects( ensureInstalledDefaultPackages(soClient, callCluster), outputService.ensureDefaultOutput(soClient), agentPolicyService.ensureDefaultAgentPolicy(soClient, esClient), + updateFleetRoleIfExists(callCluster), settingsService.getSettings(soClient).catch((e: any) => { if (e.isBoom && e.output.statusCode === 404) { const defaultSettings = createDefaultSettings(); @@ -126,15 +127,25 @@ async function createSetupSideEffects( return { isIntialized: true }; } -export async function setupFleet( - soClient: SavedObjectsClientContract, - esClient: ElasticsearchClient, - callCluster: CallESAsCurrentUser, - options?: { forceRecreate?: boolean } -) { - // Create fleet_enroll role - // This should be done directly in ES at some point - const res = await callCluster('transport.request', { +async function updateFleetRoleIfExists(callCluster: CallESAsCurrentUser) { + try { + await callCluster('transport.request', { + method: 'GET', + path: `/_security/role/${FLEET_ENROLL_ROLE}`, + }); + } catch (e) { + if (e.status === 404) { + return; + } + + throw e; + } + + return putFleetRole(callCluster); +} + +async function putFleetRole(callCluster: CallESAsCurrentUser) { + return callCluster('transport.request', { method: 'PUT', path: `/_security/role/${FLEET_ENROLL_ROLE}`, body: { @@ -156,6 +167,18 @@ export async function setupFleet( ], }, }); +} + +export async function setupFleet( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + callCluster: CallESAsCurrentUser, + options?: { forceRecreate?: boolean } +) { + // Create fleet_enroll role + // This should be done directly in ES at some point + const res = await putFleetRole(callCluster); + // If the role is already created skip the rest unless you have forceRecreate set to true if (options?.forceRecreate !== true && res.role.created === false) { return; diff --git a/x-pack/test/fleet_api_integration/apis/fleet_setup.ts b/x-pack/test/fleet_api_integration/apis/fleet_setup.ts new file mode 100644 index 0000000000000..8e9a01b28ea9b --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fleet_setup.ts @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../api_integration/ftr_provider_context'; +import { skipIfNoDockerRegistry } from '../helpers'; + +export default function (providerContext: FtrProviderContext) { + const { getService } = providerContext; + const supertest = getService('supertest'); + const es = getService('es'); + + describe('fleet_setup', () => { + skipIfNoDockerRegistry(providerContext); + beforeEach(async () => { + try { + await es.security.deleteUser({ + username: 'fleet_enroll', + }); + } catch (e) { + if (e.meta?.statusCode !== 404) { + throw e; + } + } + try { + await es.security.deleteRole({ + name: 'fleet_enroll', + }); + } catch (e) { + if (e.meta?.statusCode !== 404) { + throw e; + } + } + }); + + it('should not create a fleet_enroll role if one does not already exist', async () => { + const { body: apiResponse } = await supertest + .post(`/api/fleet/setup`) + .set('kbn-xsrf', 'xxxx') + .expect(200); + + expect(apiResponse.isInitialized).to.be(true); + + try { + await es.security.getUser({ + username: 'fleet_enroll', + }); + } catch (e) { + expect(e.meta?.statusCode).to.eql(404); + } + }); + + it('should update the fleet_enroll role with new index permissions if one does already exist', async () => { + try { + await es.security.putRole({ + name: 'fleet_enroll', + body: { + cluster: ['monitor', 'manage_api_key'], + indices: [ + { + names: [ + 'logs-*', + 'metrics-*', + 'traces-*', + '.ds-logs-*', + '.ds-metrics-*', + '.ds-traces-*', + ], + privileges: ['write', 'create_index', 'indices:admin/auto_create'], + allow_restricted_indices: false, + }, + ], + applications: [], + run_as: [], + metadata: {}, + transient_metadata: { enabled: true }, + }, + }); + } catch (e) { + if (e.meta?.statusCode !== 404) { + throw e; + } + } + + const { body: apiResponse } = await supertest + .post(`/api/fleet/setup`) + .set('kbn-xsrf', 'xxxx') + .expect(200); + + expect(apiResponse.isInitialized).to.be(true); + + const { body: roleResponse } = await es.security.getRole({ + name: 'fleet_enroll', + }); + expect(roleResponse).to.have.key('fleet_enroll'); + expect(roleResponse.fleet_enroll).to.eql({ + cluster: ['monitor', 'manage_api_key'], + indices: [ + { + names: [ + 'logs-*', + 'metrics-*', + 'traces-*', + '.ds-logs-*', + '.ds-metrics-*', + '.ds-traces-*', + '.logs-endpoint.diagnostic.collection-*', + '.ds-.logs-endpoint.diagnostic.collection-*', + ], + privileges: ['write', 'create_index', 'indices:admin/auto_create'], + allow_restricted_indices: false, + }, + ], + applications: [], + run_as: [], + metadata: {}, + transient_metadata: { enabled: true }, + }); + }); + }); +} diff --git a/x-pack/test/fleet_api_integration/apis/index.js b/x-pack/test/fleet_api_integration/apis/index.js index 0d634f60e282f..f472599652224 100644 --- a/x-pack/test/fleet_api_integration/apis/index.js +++ b/x-pack/test/fleet_api_integration/apis/index.js @@ -7,6 +7,8 @@ export default function ({ loadTestFile }) { describe('Fleet Endpoints', function () { this.tags('ciGroup10'); + // Fleet setup + loadTestFile(require.resolve('./fleet_setup')); // Agent setup loadTestFile(require.resolve('./agents_setup')); // Agents From d81ab83c1683072e4df812db4056dd2fdb382016 Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Fri, 22 Jan 2021 19:52:47 +0100 Subject: [PATCH 08/62] [examples] expressions explorer (#88344) --- ...-public.expressionsinspectoradapter.ast.md | 11 ++ ...blic.expressionsinspectoradapter.logast.md | 22 ++++ ...ions-public.expressionsinspectoradapter.md | 24 ++++ ...ibana-plugin-plugins-expressions-public.md | 1 + examples/expressions_explorer/README.md | 8 ++ examples/expressions_explorer/kibana.json | 10 ++ .../public/actions/navigate_action.ts | 21 ++++ .../public/actions/navigate_trigger.ts | 15 +++ .../public/actions_and_expressions.tsx | 102 +++++++++++++++ examples/expressions_explorer/public/app.tsx | 78 ++++++++++++ .../public/editor/expression_editor.tsx | 35 ++++++ .../public/functions/button.ts | 50 ++++++++ examples/expressions_explorer/public/index.ts | 11 ++ .../public/inspector/ast_debug_view.tsx | 78 ++++++++++++ .../inspector/expressions_inspector_view.tsx | 98 +++++++++++++++ .../expressions_inspector_view_wrapper.tsx | 17 +++ .../public/inspector/index.ts | 25 ++++ .../expressions_explorer/public/plugin.tsx | 87 +++++++++++++ .../public/render_expressions.tsx | 99 +++++++++++++++ .../public/renderers/button.tsx | 40 ++++++ .../public/run_expressions.tsx | 118 ++++++++++++++++++ examples/expressions_explorer/tsconfig.json | 18 +++ .../expressions/common/execution/execution.ts | 5 + .../util/expressions_inspector_adapter.ts | 22 ++++ src/plugins/expressions/common/util/index.ts | 1 + src/plugins/expressions/public/index.ts | 1 + src/plugins/expressions/public/public.api.md | 10 ++ test/examples/config.js | 1 + .../expressions_explorer/expressions.ts | 44 +++++++ test/examples/expressions_explorer/index.ts | 28 +++++ .../canvas_plugin_src/renderers/debug.tsx | 8 +- 31 files changed, 1085 insertions(+), 3 deletions(-) create mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md create mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md create mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md create mode 100644 examples/expressions_explorer/README.md create mode 100644 examples/expressions_explorer/kibana.json create mode 100644 examples/expressions_explorer/public/actions/navigate_action.ts create mode 100644 examples/expressions_explorer/public/actions/navigate_trigger.ts create mode 100644 examples/expressions_explorer/public/actions_and_expressions.tsx create mode 100644 examples/expressions_explorer/public/app.tsx create mode 100644 examples/expressions_explorer/public/editor/expression_editor.tsx create mode 100644 examples/expressions_explorer/public/functions/button.ts create mode 100644 examples/expressions_explorer/public/index.ts create mode 100644 examples/expressions_explorer/public/inspector/ast_debug_view.tsx create mode 100644 examples/expressions_explorer/public/inspector/expressions_inspector_view.tsx create mode 100644 examples/expressions_explorer/public/inspector/expressions_inspector_view_wrapper.tsx create mode 100644 examples/expressions_explorer/public/inspector/index.ts create mode 100644 examples/expressions_explorer/public/plugin.tsx create mode 100644 examples/expressions_explorer/public/render_expressions.tsx create mode 100644 examples/expressions_explorer/public/renderers/button.tsx create mode 100644 examples/expressions_explorer/public/run_expressions.tsx create mode 100644 examples/expressions_explorer/tsconfig.json create mode 100644 src/plugins/expressions/common/util/expressions_inspector_adapter.ts create mode 100644 test/examples/expressions_explorer/expressions.ts create mode 100644 test/examples/expressions_explorer/index.ts diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md new file mode 100644 index 0000000000000..0fdf36bc719ec --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsInspectorAdapter](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md) > [ast](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md) + +## ExpressionsInspectorAdapter.ast property + +Signature: + +```typescript +get ast(): any; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md new file mode 100644 index 0000000000000..671270a5c78ce --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsInspectorAdapter](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md) > [logAST](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md) + +## ExpressionsInspectorAdapter.logAST() method + +Signature: + +```typescript +logAST(ast: any): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | any | | + +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md new file mode 100644 index 0000000000000..23d542a0f69eb --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsInspectorAdapter](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md) + +## ExpressionsInspectorAdapter class + +Signature: + +```typescript +export declare class ExpressionsInspectorAdapter extends EventEmitter +``` + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [ast](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md) | | any | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [logAST(ast)](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md) | | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md index 1b97c9e11f83c..e3eb7a34175ee 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md @@ -16,6 +16,7 @@ | [ExpressionRenderer](./kibana-plugin-plugins-expressions-public.expressionrenderer.md) | | | [ExpressionRendererRegistry](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.md) | | | [ExpressionRenderHandler](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.md) | | +| [ExpressionsInspectorAdapter](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md) | | | [ExpressionsPublicPlugin](./kibana-plugin-plugins-expressions-public.expressionspublicplugin.md) | | | [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) | ExpressionsService class is used for multiple purposes:1. It implements the same Expressions service that can be used on both: (1) server-side and (2) browser-side. 2. It implements the same Expressions service that users can fork/clone, thus have their own instance of the Expressions plugin. 3. ExpressionsService defines the public contracts of \*setup\* and \*start\* Kibana Platform life-cycles for ease-of-use on server-side and browser-side. 4. ExpressionsService creates a bound version of all exported contract functions. 5. Functions are bound the way there are:\`\`\`ts registerFunction = (...args: Parameters<Executor\['registerFunction'\]> ): ReturnType<Executor\['registerFunction'\]> => this.executor.registerFunction(...args); \`\`\`so that JSDoc appears in developers IDE when they use those plugins.expressions.registerFunction(. | | [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) | | diff --git a/examples/expressions_explorer/README.md b/examples/expressions_explorer/README.md new file mode 100644 index 0000000000000..ead0ca758f8e5 --- /dev/null +++ b/examples/expressions_explorer/README.md @@ -0,0 +1,8 @@ +## expressions explorer + +This example expressions explorer app shows how to: + - to run expression + - to render expression output + - emit events from expression renderer and handle them + +To run this example, use the command `yarn start --run-examples`. \ No newline at end of file diff --git a/examples/expressions_explorer/kibana.json b/examples/expressions_explorer/kibana.json new file mode 100644 index 0000000000000..038b7eea0ef21 --- /dev/null +++ b/examples/expressions_explorer/kibana.json @@ -0,0 +1,10 @@ +{ + "id": "expressionsExplorer", + "version": "0.0.1", + "kibanaVersion": "kibana", + "server": false, + "ui": true, + "requiredPlugins": ["expressions", "inspector", "uiActions", "developerExamples"], + "optionalPlugins": [], + "requiredBundles": [] +} diff --git a/examples/expressions_explorer/public/actions/navigate_action.ts b/examples/expressions_explorer/public/actions/navigate_action.ts new file mode 100644 index 0000000000000..d29a9e6b345b6 --- /dev/null +++ b/examples/expressions_explorer/public/actions/navigate_action.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { createAction } from '../../../../src/plugins/ui_actions/public'; + +export const ACTION_NAVIGATE = 'ACTION_NAVIGATE'; + +export const createNavigateAction = () => + createAction({ + id: ACTION_NAVIGATE, + type: ACTION_NAVIGATE, + getDisplayName: () => 'Navigate', + execute: async (event: any) => { + window.location.href = event.href; + }, + }); diff --git a/examples/expressions_explorer/public/actions/navigate_trigger.ts b/examples/expressions_explorer/public/actions/navigate_trigger.ts new file mode 100644 index 0000000000000..eacbd968eaa93 --- /dev/null +++ b/examples/expressions_explorer/public/actions/navigate_trigger.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { Trigger } from '../../../../src/plugins/ui_actions/public'; + +export const NAVIGATE_TRIGGER_ID = 'NAVIGATE_TRIGGER_ID'; + +export const navigateTrigger: Trigger = { + id: NAVIGATE_TRIGGER_ID, +}; diff --git a/examples/expressions_explorer/public/actions_and_expressions.tsx b/examples/expressions_explorer/public/actions_and_expressions.tsx new file mode 100644 index 0000000000000..6e2eebcde4a0f --- /dev/null +++ b/examples/expressions_explorer/public/actions_and_expressions.tsx @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React, { useState } from 'react'; +import { + EuiFlexItem, + EuiFlexGroup, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiPageHeader, + EuiPageHeaderSection, + EuiPanel, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { + ExpressionsStart, + ReactExpressionRenderer, + ExpressionsInspectorAdapter, +} from '../../../src/plugins/expressions/public'; +import { ExpressionEditor } from './editor/expression_editor'; +import { UiActionsStart } from '../../../src/plugins/ui_actions/public'; +import { NAVIGATE_TRIGGER_ID } from './actions/navigate_trigger'; + +interface Props { + expressions: ExpressionsStart; + actions: UiActionsStart; +} + +export function ActionsExpressionsExample({ expressions, actions }: Props) { + const [expression, updateExpression] = useState( + 'button name="click me" href="http://www.google.com"' + ); + + const expressionChanged = (value: string) => { + updateExpression(value); + }; + + const inspectorAdapters = { + expression: new ExpressionsInspectorAdapter(), + }; + + const handleEvents = (event: any) => { + if (event.id !== 'NAVIGATE') return; + // enrich event context with some extra data + event.baseUrl = 'http://www.google.com'; + + actions.executeTriggerActions(NAVIGATE_TRIGGER_ID, event.value); + }; + + return ( + + + + +

Actions from expression renderers

+ + + + + + + + + Here you can play with sample `button` which takes a url as configuration and + displays a button which emits custom BUTTON_CLICK trigger to which we have attached + a custom action which performs the navigation. + + + + + + + + + + + + + { + return
{message}
; + }} + /> +
+
+
+
+
+ + ); +} diff --git a/examples/expressions_explorer/public/app.tsx b/examples/expressions_explorer/public/app.tsx new file mode 100644 index 0000000000000..d72cf08128a5a --- /dev/null +++ b/examples/expressions_explorer/public/app.tsx @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { + EuiPage, + EuiPageHeader, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiSpacer, + EuiText, + EuiLink, +} from '@elastic/eui'; +import { AppMountParameters } from '../../../src/core/public'; +import { ExpressionsStart } from '../../../src/plugins/expressions/public'; +import { Start as InspectorStart } from '../../../src/plugins/inspector/public'; +import { RunExpressionsExample } from './run_expressions'; +import { RenderExpressionsExample } from './render_expressions'; +import { ActionsExpressionsExample } from './actions_and_expressions'; +import { UiActionsStart } from '../../../src/plugins/ui_actions/public'; + +interface Props { + expressions: ExpressionsStart; + inspector: InspectorStart; + actions: UiActionsStart; +} + +const ExpressionsExplorer = ({ expressions, inspector, actions }: Props) => { + return ( + + + Expressions Explorer + + + +

+ There are a couple of ways to run the expressions. Below some of the options are + demonstrated. You can read more about it{' '} + + here + +

+
+ + + + + + + + + + + + +
+
+
+
+ ); +}; + +export const renderApp = (props: Props, { element }: AppMountParameters) => { + ReactDOM.render(, element); + + return () => ReactDOM.unmountComponentAtNode(element); +}; diff --git a/examples/expressions_explorer/public/editor/expression_editor.tsx b/examples/expressions_explorer/public/editor/expression_editor.tsx new file mode 100644 index 0000000000000..e3dbb5998b92e --- /dev/null +++ b/examples/expressions_explorer/public/editor/expression_editor.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React from 'react'; +import { EuiCodeEditor } from '@elastic/eui'; + +interface Props { + value: string; + onChange: (value: string) => void; +} + +export function ExpressionEditor({ value, onChange }: Props) { + return ( + {}} + aria-label="Code Editor" + /> + ); +} diff --git a/examples/expressions_explorer/public/functions/button.ts b/examples/expressions_explorer/public/functions/button.ts new file mode 100644 index 0000000000000..8c39aa2743b30 --- /dev/null +++ b/examples/expressions_explorer/public/functions/button.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../src/plugins/expressions/common'; + +interface Arguments { + href: string; + name: string; +} + +export type ExpressionFunctionButton = ExpressionFunctionDefinition< + 'button', + unknown, + Arguments, + unknown +>; + +export const buttonFn: ExpressionFunctionButton = { + name: 'button', + args: { + href: { + help: i18n.translate('expressions.functions.font.args.href', { + defaultMessage: 'Link to which to navigate', + }), + types: ['string'], + required: true, + }, + name: { + help: i18n.translate('expressions.functions.font.args.name', { + defaultMessage: 'Name of the button', + }), + types: ['string'], + default: 'button', + }, + }, + help: 'Configures the button', + fn: (input: unknown, args: Arguments) => { + return { + type: 'render', + as: 'button', + value: args, + }; + }, +}; diff --git a/examples/expressions_explorer/public/index.ts b/examples/expressions_explorer/public/index.ts new file mode 100644 index 0000000000000..a6dbbc9198f44 --- /dev/null +++ b/examples/expressions_explorer/public/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { ExpressionsExplorerPlugin } from './plugin'; + +export const plugin = () => new ExpressionsExplorerPlugin(); diff --git a/examples/expressions_explorer/public/inspector/ast_debug_view.tsx b/examples/expressions_explorer/public/inspector/ast_debug_view.tsx new file mode 100644 index 0000000000000..d860ff30bd8e9 --- /dev/null +++ b/examples/expressions_explorer/public/inspector/ast_debug_view.tsx @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React, { useState } from 'react'; +import { EuiTreeView, EuiDescriptionList, EuiCodeBlock, EuiText, EuiSpacer } from '@elastic/eui'; + +interface Props { + ast: any; +} + +const decorateAst = (ast: any, nodeClicked: any) => { + return ast.chain.map((link: any) => { + return { + id: link.function + Math.random(), + label: link.function, + callback: () => { + nodeClicked(link.debug); + }, + children: Object.keys(link.arguments).reduce((result: any, key: string) => { + if (typeof link.arguments[key] === 'object') { + // result[key] = decorateAst(link.arguments[key]); + } + return result; + }, []), + }; + }); +}; + +const prepareNode = (key: string, value: any) => { + if (key === 'args') { + return ( + + {JSON.stringify(value, null, '\t')} + + ); + } else if (key === 'output' || key === 'input') { + return ( + + {JSON.stringify(value, null, '\t')} + + ); + } else if (key === 'success') { + return value ? 'true' : 'false'; + } else return {value}; +}; + +export function AstDebugView({ ast }: Props) { + const [nodeInfo, setNodeInfo] = useState([] as any[]); + const items = decorateAst(ast, (node: any) => { + setNodeInfo( + Object.keys(node).map((key) => ({ + title: key, + description: prepareNode(key, node[key]), + })) + ); + }); + + return ( +
+ List of executed expression functions: + + + Details of selected function: + +
+ ); +} diff --git a/examples/expressions_explorer/public/inspector/expressions_inspector_view.tsx b/examples/expressions_explorer/public/inspector/expressions_inspector_view.tsx new file mode 100644 index 0000000000000..1233735072d04 --- /dev/null +++ b/examples/expressions_explorer/public/inspector/expressions_inspector_view.tsx @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiEmptyPrompt } from '@elastic/eui'; +import { InspectorViewProps, Adapters } from '../../../../src/plugins/inspector/public'; +import { AstDebugView } from './ast_debug_view'; + +interface ExpressionsInspectorViewComponentState { + ast: any; + adapters: Adapters; +} + +class ExpressionsInspectorViewComponent extends Component< + InspectorViewProps, + ExpressionsInspectorViewComponentState +> { + static propTypes = { + adapters: PropTypes.object.isRequired, + title: PropTypes.string.isRequired, + }; + + state = {} as ExpressionsInspectorViewComponentState; + + static getDerivedStateFromProps( + nextProps: Readonly, + state: ExpressionsInspectorViewComponentState + ) { + if (state && nextProps.adapters === state.adapters) { + return null; + } + + const { ast } = nextProps.adapters.expression; + + return { + adapters: nextProps.adapters, + ast, + }; + } + + onUpdateData = (ast: any) => { + this.setState({ + ast, + }); + }; + + componentDidMount() { + this.props.adapters.expression!.on('change', this.onUpdateData); + } + + componentWillUnmount() { + this.props.adapters.expression!.removeListener('change', this.onUpdateData); + } + + static renderNoData() { + return ( + + + + } + body={ + +

+ +

+
+ } + /> + ); + } + + render() { + if (!this.state.ast) { + return ExpressionsInspectorViewComponent.renderNoData(); + } + + return ; + } +} + +// default export required for React.Lazy +// eslint-disable-next-line import/no-default-export +export default ExpressionsInspectorViewComponent; diff --git a/examples/expressions_explorer/public/inspector/expressions_inspector_view_wrapper.tsx b/examples/expressions_explorer/public/inspector/expressions_inspector_view_wrapper.tsx new file mode 100644 index 0000000000000..b10c82e5df309 --- /dev/null +++ b/examples/expressions_explorer/public/inspector/expressions_inspector_view_wrapper.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React, { lazy } from 'react'; + +const ExpressionsInspectorViewComponent = lazy(() => import('./expressions_inspector_view')); + +export const getExpressionsInspectorViewComponentWrapper = () => { + return (props: any) => { + return ; + }; +}; diff --git a/examples/expressions_explorer/public/inspector/index.ts b/examples/expressions_explorer/public/inspector/index.ts new file mode 100644 index 0000000000000..ec87a1240ac74 --- /dev/null +++ b/examples/expressions_explorer/public/inspector/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { Adapters, InspectorViewDescription } from '../../../../src/plugins/inspector/public'; +import { getExpressionsInspectorViewComponentWrapper } from './expressions_inspector_view_wrapper'; + +export const getExpressionsInspectorViewDescription = (): InspectorViewDescription => ({ + title: i18n.translate('data.inspector.table.dataTitle', { + defaultMessage: 'Expression', + }), + order: 100, + help: i18n.translate('data.inspector.table..dataDescriptionTooltip', { + defaultMessage: 'View the expression behind the visualization', + }), + shouldShow(adapters: Adapters) { + return Boolean(adapters.expression); + }, + component: getExpressionsInspectorViewComponentWrapper(), +}); diff --git a/examples/expressions_explorer/public/plugin.tsx b/examples/expressions_explorer/public/plugin.tsx new file mode 100644 index 0000000000000..9643389ad881c --- /dev/null +++ b/examples/expressions_explorer/public/plugin.tsx @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { Plugin, CoreSetup, AppMountParameters, AppNavLinkStatus } from '../../../src/core/public'; +import { DeveloperExamplesSetup } from '../../developer_examples/public'; +import { ExpressionsSetup, ExpressionsStart } from '../../../src/plugins/expressions/public'; +import { + Setup as InspectorSetup, + Start as InspectorStart, +} from '../../../src/plugins/inspector/public'; +import { getExpressionsInspectorViewDescription } from './inspector'; +import { UiActionsStart, UiActionsSetup } from '../../../src/plugins/ui_actions/public'; +import { NAVIGATE_TRIGGER_ID, navigateTrigger } from './actions/navigate_trigger'; +import { ACTION_NAVIGATE, createNavigateAction } from './actions/navigate_action'; +import { buttonRenderer } from './renderers/button'; +import { buttonFn } from './functions/button'; + +interface StartDeps { + expressions: ExpressionsStart; + inspector: InspectorStart; + uiActions: UiActionsStart; +} + +interface SetupDeps { + uiActions: UiActionsSetup; + expressions: ExpressionsSetup; + inspector: InspectorSetup; + developerExamples: DeveloperExamplesSetup; +} + +export class ExpressionsExplorerPlugin implements Plugin { + public setup(core: CoreSetup, deps: SetupDeps) { + // register custom inspector adapter & view + deps.inspector.registerView(getExpressionsInspectorViewDescription()); + + // register custom actions + deps.uiActions.registerTrigger(navigateTrigger); + deps.uiActions.registerAction(createNavigateAction()); + deps.uiActions.attachAction(NAVIGATE_TRIGGER_ID, ACTION_NAVIGATE); + + // register custom functions and renderers + deps.expressions.registerRenderer(buttonRenderer); + deps.expressions.registerFunction(buttonFn); + + core.application.register({ + id: 'expressionsExplorer', + title: 'Expressions Explorer', + navLinkStatus: AppNavLinkStatus.hidden, + async mount(params: AppMountParameters) { + const [, depsStart] = await core.getStartServices(); + const { renderApp } = await import('./app'); + return renderApp( + { + expressions: depsStart.expressions, + inspector: depsStart.inspector, + actions: depsStart.uiActions, + }, + params + ); + }, + }); + + deps.developerExamples.register({ + appId: 'expressionsExplorer', + title: 'Expressions', + description: `Expressions is a plugin that allows to execute Kibana expressions and render content using expression renderers. This example plugin showcases various usage scenarios.`, + links: [ + { + label: 'README', + href: 'https://github.com/elastic/kibana/blob/master/src/plugins/expressions/README.md', + iconType: 'logoGithub', + size: 's', + target: '_blank', + }, + ], + }); + } + + public start() {} + + public stop() {} +} diff --git a/examples/expressions_explorer/public/render_expressions.tsx b/examples/expressions_explorer/public/render_expressions.tsx new file mode 100644 index 0000000000000..ffbe558f30218 --- /dev/null +++ b/examples/expressions_explorer/public/render_expressions.tsx @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React, { useState } from 'react'; +import { + EuiFlexItem, + EuiFlexGroup, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiPageHeader, + EuiPageHeaderSection, + EuiPanel, + EuiText, + EuiTitle, + EuiButton, +} from '@elastic/eui'; +import { + ExpressionsStart, + ReactExpressionRenderer, + ExpressionsInspectorAdapter, +} from '../../../src/plugins/expressions/public'; +import { ExpressionEditor } from './editor/expression_editor'; +import { Start as InspectorStart } from '../../../src/plugins/inspector/public'; + +interface Props { + expressions: ExpressionsStart; + inspector: InspectorStart; +} + +export function RenderExpressionsExample({ expressions, inspector }: Props) { + const [expression, updateExpression] = useState('markdown "## expressions explorer rendering"'); + + const expressionChanged = (value: string) => { + updateExpression(value); + }; + + const inspectorAdapters = { + expression: new ExpressionsInspectorAdapter(), + }; + + return ( + + + + +

Render expressions

+
+
+
+ + + + + + In the below editor you can enter your expression and render it. Using + ReactExpressionRenderer component makes that very easy. + + + + { + inspector.open(inspectorAdapters); + }} + > + Open Inspector + + + + + + + + + + + + + { + return
{message}
; + }} + /> +
+
+
+
+
+
+ ); +} diff --git a/examples/expressions_explorer/public/renderers/button.tsx b/examples/expressions_explorer/public/renderers/button.tsx new file mode 100644 index 0000000000000..32f1f31894dce --- /dev/null +++ b/examples/expressions_explorer/public/renderers/button.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import ReactDOM from 'react-dom'; +import React from 'react'; +import { EuiButton } from '@elastic/eui'; +import { ExpressionRenderDefinition } from '../../../../src/plugins/expressions/common/expression_renderers'; + +export const buttonRenderer: ExpressionRenderDefinition = { + name: 'button', + displayName: 'Button', + reuseDomNode: true, + render(domNode, config, handlers) { + const buttonClick = () => { + handlers.event({ + id: 'NAVIGATE', + value: { + href: config.href, + }, + }); + }; + + const renderDebug = () => ( +
+ + {config.name} + +
+ ); + + ReactDOM.render(renderDebug(), domNode, () => handlers.done()); + + handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); + }, +}; diff --git a/examples/expressions_explorer/public/run_expressions.tsx b/examples/expressions_explorer/public/run_expressions.tsx new file mode 100644 index 0000000000000..efbdbc2d41836 --- /dev/null +++ b/examples/expressions_explorer/public/run_expressions.tsx @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React, { useState, useEffect, useMemo } from 'react'; +import { + EuiCodeBlock, + EuiFlexItem, + EuiFlexGroup, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiPageHeader, + EuiPageHeaderSection, + EuiPanel, + EuiText, + EuiTitle, + EuiButton, +} from '@elastic/eui'; +import { + ExpressionsStart, + ExpressionsInspectorAdapter, +} from '../../../src/plugins/expressions/public'; +import { ExpressionEditor } from './editor/expression_editor'; +import { Start as InspectorStart } from '../../../src/plugins/inspector/public'; + +interface Props { + expressions: ExpressionsStart; + inspector: InspectorStart; +} + +export function RunExpressionsExample({ expressions, inspector }: Props) { + const [expression, updateExpression] = useState('markdown "## expressions explorer"'); + const [result, updateResult] = useState({}); + + const expressionChanged = (value: string) => { + updateExpression(value); + }; + + const inspectorAdapters = useMemo( + () => ({ + expression: new ExpressionsInspectorAdapter(), + }), + [] + ); + + useEffect(() => { + const runExpression = async () => { + const execution = expressions.execute(expression, null, { + debug: true, + inspectorAdapters, + }); + + const data: any = await execution.getData(); + updateResult(data); + }; + + runExpression(); + }, [expression, expressions, inspectorAdapters]); + + return ( + + + + +

Run expressions

+
+
+
+ + + + + + In the below editor you can enter your expression and execute it. Using + expressions.execute allows you to easily run the expression. + + + + { + inspector.open(inspectorAdapters); + }} + > + Open Inspector + + + + + + + + + + + + + + {JSON.stringify(result, null, '\t')} + + + + + + +
+ ); +} diff --git a/examples/expressions_explorer/tsconfig.json b/examples/expressions_explorer/tsconfig.json new file mode 100644 index 0000000000000..b4449819b25a6 --- /dev/null +++ b/examples/expressions_explorer/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "../../typings/**/*", + ], + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" }, + { "path": "../../src/plugins/kibana_react/tsconfig.json" }, + ] +} diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts index 8e068818ec0ce..0240ec90cb1e6 100644 --- a/src/plugins/expressions/common/execution/execution.ts +++ b/src/plugins/expressions/common/execution/execution.ts @@ -29,6 +29,7 @@ import { getByAlias } from '../util/get_by_alias'; import { ExecutionContract } from './execution_contract'; import { ExpressionExecutionParams } from '../service'; import { TablesAdapter } from '../util/tables_adapter'; +import { ExpressionsInspectorAdapter } from '../util/expressions_inspector_adapter'; /** * AbortController is not available in Node until v15, so we @@ -63,6 +64,7 @@ export interface ExecutionParams { const createDefaultInspectorAdapters = (): DefaultInspectorAdapters => ({ requests: new RequestAdapter(), tables: new TablesAdapter(), + expression: new ExpressionsInspectorAdapter(), }); export class Execution< @@ -208,6 +210,9 @@ export class Execution< this.firstResultFuture.promise .then( (result) => { + if (this.context.inspectorAdapters.expression) { + this.context.inspectorAdapters.expression.logAST(this.state.get().ast); + } this.state.transitions.setResult(result); }, (error) => { diff --git a/src/plugins/expressions/common/util/expressions_inspector_adapter.ts b/src/plugins/expressions/common/util/expressions_inspector_adapter.ts new file mode 100644 index 0000000000000..c82884d373d2f --- /dev/null +++ b/src/plugins/expressions/common/util/expressions_inspector_adapter.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { EventEmitter } from 'events'; + +export class ExpressionsInspectorAdapter extends EventEmitter { + private _ast: any = {}; + + public logAST(ast: any): void { + this._ast = ast; + this.emit('change', this._ast); + } + + public get ast() { + return this._ast; + } +} diff --git a/src/plugins/expressions/common/util/index.ts b/src/plugins/expressions/common/util/index.ts index ecb7d5cdca81e..4762f9979fe4a 100644 --- a/src/plugins/expressions/common/util/index.ts +++ b/src/plugins/expressions/common/util/index.ts @@ -9,3 +9,4 @@ export * from './create_error'; export * from './get_by_alias'; export * from './tables_adapter'; +export * from './expressions_inspector_adapter'; diff --git a/src/plugins/expressions/public/index.ts b/src/plugins/expressions/public/index.ts index 9485daf49c981..d6dd2fc1f3d37 100644 --- a/src/plugins/expressions/public/index.ts +++ b/src/plugins/expressions/public/index.ts @@ -107,4 +107,5 @@ export { ExpressionsServiceSetup, ExpressionsServiceStart, TablesAdapter, + ExpressionsInspectorAdapter, } from '../common'; diff --git a/src/plugins/expressions/public/public.api.md b/src/plugins/expressions/public/public.api.md index 7fa0857be8aba..029d727e82e74 100644 --- a/src/plugins/expressions/public/public.api.md +++ b/src/plugins/expressions/public/public.api.md @@ -551,6 +551,16 @@ export class ExpressionRenderHandler { update$: Observable; } +// Warning: (ae-missing-release-tag) "ExpressionsInspectorAdapter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class ExpressionsInspectorAdapter extends EventEmitter { + // (undocumented) + get ast(): any; + // (undocumented) + logAST(ast: any): void; +} + // Warning: (ae-missing-release-tag) "ExpressionsPublicPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/test/examples/config.js b/test/examples/config.js index a720899a637de..aab71cb305016 100644 --- a/test/examples/config.js +++ b/test/examples/config.js @@ -19,6 +19,7 @@ export default async function ({ readConfigFile }) { require.resolve('./ui_actions'), require.resolve('./state_sync'), require.resolve('./routing'), + require.resolve('./expressions_explorer'), ], services: { ...functionalConfig.get('services'), diff --git a/test/examples/expressions_explorer/expressions.ts b/test/examples/expressions_explorer/expressions.ts new file mode 100644 index 0000000000000..7261564e6db38 --- /dev/null +++ b/test/examples/expressions_explorer/expressions.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import expect from '@kbn/expect'; + +import { PluginFunctionalProviderContext } from 'test/plugin_functional/services'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getService }: PluginFunctionalProviderContext) { + const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + const browser = getService('browser'); + + describe('', () => { + it('runs expression', async () => { + await retry.try(async () => { + const text = await testSubjects.getVisibleText('expressionResult'); + expect(text).to.be( + '{\n "type": "error",\n "error": {\n "message": "Function markdown could not be found.",\n "name": "fn not found"\n }\n}' + ); + }); + }); + + it('renders expression', async () => { + await retry.try(async () => { + const text = await testSubjects.getVisibleText('expressionRender'); + expect(text).to.be('Function markdown could not be found.'); + }); + }); + + it('emits an action and navigates', async () => { + await testSubjects.click('testExpressionButton'); + await retry.try(async () => { + const text = await browser.getCurrentUrl(); + expect(text).to.be('https://www.google.com/?gws_rd=ssl'); + }); + }); + }); +} diff --git a/test/examples/expressions_explorer/index.ts b/test/examples/expressions_explorer/index.ts new file mode 100644 index 0000000000000..77d2a594c0f29 --- /dev/null +++ b/test/examples/expressions_explorer/index.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { PluginFunctionalProviderContext } from 'test/plugin_functional/services'; + +// eslint-disable-next-line import/no-default-export +export default function ({ + getService, + getPageObjects, + loadTestFile, +}: PluginFunctionalProviderContext) { + const browser = getService('browser'); + const PageObjects = getPageObjects(['common', 'header']); + + describe('expressions explorer', function () { + before(async () => { + await browser.setWindowSize(1300, 900); + await PageObjects.common.navigateToApp('expressionsExplorer'); + }); + + loadTestFile(require.resolve('./expressions')); + }); +} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/debug.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/debug.tsx index b4fbba96e8dfb..341913a033c05 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/debug.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/debug.tsx @@ -26,9 +26,11 @@ export const debug: RendererFactory = () => ({ ReactDOM.render(renderDebug(), domNode, () => handlers.done()); - handlers.onResize(() => { - ReactDOM.render(renderDebug(), domNode, () => handlers.done()); - }); + if (handlers.onResize) { + handlers.onResize(() => { + ReactDOM.render(renderDebug(), domNode, () => handlers.done()); + }); + } handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); }, From 8263d47d378315bdcad990620010c8452f3133d1 Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Fri, 22 Jan 2021 14:19:09 -0500 Subject: [PATCH 09/62] Fix sharing saved objects phase 2 CI (#89056) --- .../migrations/core/document_migrator.test.ts | 14 ++++++++++++++ .../migrations/core/document_migrator.ts | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts index 741f715ba6ebe..6ba652abda3d5 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts @@ -206,6 +206,20 @@ describe('DocumentMigrator', () => { ); }); + it('coerces the current Kibana version if it has a hyphen', () => { + const validDefinition = { + kibanaVersion: '3.2.0-SNAPSHOT', + typeRegistry: createRegistry({ + name: 'foo', + convertToMultiNamespaceTypeVersion: '3.2.0', + namespaceType: 'multiple', + }), + minimumConvertVersion: '0.0.0', + log: mockLogger, + }; + expect(() => new DocumentMigrator(validDefinition)).not.toThrowError(); + }); + it('validates convertToMultiNamespaceTypeVersion is not used on a patch version', () => { const invalidDefinition = { kibanaVersion: '3.2.3', diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.ts b/src/core/server/saved_objects/migrations/core/document_migrator.ts index e4b89a949d3cf..e93586ec7ce4c 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.ts @@ -159,10 +159,11 @@ export class DocumentMigrator implements VersionedTransformer { */ constructor({ typeRegistry, - kibanaVersion, + kibanaVersion: rawKibanaVersion, minimumConvertVersion = DEFAULT_MINIMUM_CONVERT_VERSION, log, }: DocumentMigratorOptions) { + const kibanaVersion = rawKibanaVersion.split('-')[0]; // coerce a semver-like string (x.y.z-SNAPSHOT) or prerelease version (x.y.z-alpha) to a regular semver (x.y.z) validateMigrationDefinition(typeRegistry, kibanaVersion, minimumConvertVersion); this.documentMigratorOptions = { typeRegistry, kibanaVersion, log }; From c739f437ddcf48af1fb5719f0034fe1ef45f31b0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 25 Jan 2021 10:36:21 +0200 Subject: [PATCH 10/62] Update dependency vega to ^5.19.1 (#88984) Co-authored-by: Renovate Bot Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 60 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 87e0f84695235..2fdc31820b9d4 100644 --- a/package.json +++ b/package.json @@ -828,7 +828,7 @@ "url-loader": "^2.2.0", "use-resize-observer": "^6.0.0", "val-loader": "^1.1.1", - "vega": "^5.18.0", + "vega": "^5.19.1", "vega-lite": "^4.17.0", "vega-schema-url-parser": "^2.1.0", "vega-tooltip": "^0.25.0", diff --git a/yarn.lock b/yarn.lock index cc32349b10860..828a3b630a838 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28645,10 +28645,10 @@ vega-functions@^5.10.0: vega-time "^2.0.4" vega-util "^1.16.0" -vega-functions@~5.11.0: - version "5.11.0" - resolved "https://registry.yarnpkg.com/vega-functions/-/vega-functions-5.11.0.tgz#a590d016f93c81730bdbc336b377231d7ae48569" - integrity sha512-/p0QIDiA3RaUZ7drxHuClpDQCrIScSHJlY0oo0+GFYGfp+lvb29Ox1T4a+wtqeCp6NRaTWry+EwDxojnshTZIQ== +vega-functions@^5.12.0, vega-functions@~5.12.0: + version "5.12.0" + resolved "https://registry.yarnpkg.com/vega-functions/-/vega-functions-5.12.0.tgz#44bf08a7b20673dc8cf51d6781c8ea1399501668" + integrity sha512-3hljmGs+gR7TbO/yYuvAP9P5laKISf1GKk4yRHLNdM61fWgKm8pI3f6LY2Hvq9cHQFTiJ3/5/Bx2p1SX5R4quQ== dependencies: d3-array "^2.7.1" d3-color "^2.0.0" @@ -28656,8 +28656,8 @@ vega-functions@~5.11.0: vega-dataflow "^5.7.3" vega-expression "^4.0.1" vega-scale "^7.1.1" - vega-scenegraph "^4.9.2" - vega-selections "^5.2.0" + vega-scenegraph "^4.9.3" + vega-selections "^5.3.0" vega-statistics "^1.7.9" vega-time "^2.0.4" vega-util "^1.16.0" @@ -28724,16 +28724,16 @@ vega-loader@^4.3.2, vega-loader@^4.3.3, vega-loader@~4.4.0: vega-format "^1.0.4" vega-util "^1.16.0" -vega-parser@~6.1.2: - version "6.1.2" - resolved "https://registry.yarnpkg.com/vega-parser/-/vega-parser-6.1.2.tgz#7f25751177e38c3239560a9c427ded8d2ba617bb" - integrity sha512-aGyZrNzPrBruEb/WhemKDuDjQsIkMDGIgnSJci0b+9ZVxjyAzMl7UfGbiYorPiJlnIercjUJbMoFD6fCIf4gqQ== +vega-parser@~6.1.3: + version "6.1.3" + resolved "https://registry.yarnpkg.com/vega-parser/-/vega-parser-6.1.3.tgz#df72785e4b086eceb90ee6219a399210933b507b" + integrity sha512-8oiVhhW26GQ4GZBvolId8FVFvhn3s1KGgPlD7Z+4P2wkV+xe5Nqu0TEJ20F/cn3b88fd0Vj48X3BH3dlSeKNFg== dependencies: vega-dataflow "^5.7.3" vega-event-selector "^2.0.6" - vega-functions "^5.10.0" + vega-functions "^5.12.0" vega-scale "^7.1.1" - vega-util "^1.15.2" + vega-util "^1.16.0" vega-projection@^1.4.5, vega-projection@~1.4.5: version "1.4.5" @@ -28772,7 +28772,7 @@ vega-scale@^7.0.3, vega-scale@^7.1.1, vega-scale@~7.1.1: vega-time "^2.0.4" vega-util "^1.15.2" -vega-scenegraph@^4.9.2, vega-scenegraph@~4.9.2: +vega-scenegraph@^4.9.2: version "4.9.2" resolved "https://registry.yarnpkg.com/vega-scenegraph/-/vega-scenegraph-4.9.2.tgz#83b1dbc34a9ab5595c74d547d6d95849d74451ed" integrity sha512-epm1CxcB8AucXQlSDeFnmzy0FCj+HV2k9R6ch2lfLRln5lPLEfgJWgFcFhVf5jyheY0FSeHH52Q5zQn1vYI1Ow== @@ -28784,6 +28784,18 @@ vega-scenegraph@^4.9.2, vega-scenegraph@~4.9.2: vega-scale "^7.1.1" vega-util "^1.15.2" +vega-scenegraph@^4.9.3, vega-scenegraph@~4.9.3: + version "4.9.3" + resolved "https://registry.yarnpkg.com/vega-scenegraph/-/vega-scenegraph-4.9.3.tgz#c4720550ea7ff5c8d9d0690f47fe2640547cfc6b" + integrity sha512-lBvqLbXqrqRCTGJmSgzZC/tLR/o+TXfakbdhDzNdpgTavTaQ65S/67Gpj5hPpi77DvsfZUIY9lCEeO37aJhy0Q== + dependencies: + d3-path "^2.0.0" + d3-shape "^2.0.0" + vega-canvas "^1.2.5" + vega-loader "^4.3.3" + vega-scale "^7.1.1" + vega-util "^1.15.2" + vega-schema-url-parser@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/vega-schema-url-parser/-/vega-schema-url-parser-2.1.0.tgz#847f9cf9f1624f36f8a51abc1adb41ebc6673cb4" @@ -28797,10 +28809,10 @@ vega-selections@^5.1.5: vega-expression "^4.0.0" vega-util "^1.15.2" -vega-selections@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/vega-selections/-/vega-selections-5.2.0.tgz#d85968d1bccc175fd92661c91d88151ffd5ade83" - integrity sha512-Xf3nTTJHRGw4tQMbt+0sBI/7WkEIzPG9E4HXkZk5Y9Q2HsGRVLmrAEXHSfpENrBLWTBZk/uvmP9rKDG7cbcTrg== +vega-selections@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/vega-selections/-/vega-selections-5.3.0.tgz#810f2e7b7642fa836cf98b2e5dcc151093b1f6a7" + integrity sha512-vC4NPsuN+IffruFXfH0L3i2A51RgG4PqpLv85TvrEAIYnSkyKDE4bf+wVraR3aPdnLLkc3+tYuMi6le5FmThIA== dependencies: vega-expression "^4.0.1" vega-util "^1.16.0" @@ -28899,10 +28911,10 @@ vega-wordcloud@~4.1.3: vega-statistics "^1.7.9" vega-util "^1.15.2" -vega@^5.18.0: - version "5.18.0" - resolved "https://registry.yarnpkg.com/vega/-/vega-5.18.0.tgz#98645e5d3bd5267d66ea3e701d99dcff63cfff8a" - integrity sha512-ysqouhboWNXSuQNN7W5IGOXsnEJNFVX5duCi0tTwRsFLc61FshpqVh4+4VoXg5pH0ZCxwpqbOwd2ULZWjJTx6g== +vega@^5.19.1: + version "5.19.1" + resolved "https://registry.yarnpkg.com/vega/-/vega-5.19.1.tgz#64c8350740fe1a11d56cc6617ab3a76811fd704c" + integrity sha512-UE6/c9q9kzuz4HULFuU9HscBASoZa+zcXqGKdbQP545Nwmhd078QpcH+wZsq9lYfiTxmFtzLK/a0OH0zhkghvA== dependencies: vega-crossfilter "~4.0.5" vega-dataflow "~5.7.3" @@ -28911,17 +28923,17 @@ vega@^5.18.0: vega-expression "~4.0.1" vega-force "~4.0.7" vega-format "~1.0.4" - vega-functions "~5.11.0" + vega-functions "~5.12.0" vega-geo "~4.3.8" vega-hierarchy "~4.0.9" vega-label "~1.0.0" vega-loader "~4.4.0" - vega-parser "~6.1.2" + vega-parser "~6.1.3" vega-projection "~1.4.5" vega-regression "~1.0.9" vega-runtime "~6.1.3" vega-scale "~7.1.1" - vega-scenegraph "~4.9.2" + vega-scenegraph "~4.9.3" vega-statistics "~1.7.9" vega-time "~2.0.4" vega-transforms "~4.9.3" From 02f24de89994c34dcc75ba30c44c56731ffdbe13 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 25 Jan 2021 11:25:46 +0100 Subject: [PATCH 11/62] [Lens] Add drag drop tests (#88660) --- .../functional/apps/lens/drag_and_drop.ts | 153 +++-- .../es_archives/lens/basic/data.json | 577 ++++++++++++++++++ .../test/functional/page_objects/lens_page.ts | 3 + 3 files changed, 674 insertions(+), 59 deletions(-) create mode 100644 x-pack/test/functional/es_archives/lens/basic/data.json diff --git a/x-pack/test/functional/apps/lens/drag_and_drop.ts b/x-pack/test/functional/apps/lens/drag_and_drop.ts index e0130bc394271..57e5990a74012 100644 --- a/x-pack/test/functional/apps/lens/drag_and_drop.ts +++ b/x-pack/test/functional/apps/lens/drag_and_drop.ts @@ -11,73 +11,108 @@ export default function ({ getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); describe('lens drag and drop tests', () => { - it('should construct the basic split xy chart', async () => { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickVisType('lens'); - await PageObjects.lens.goToTimeRange(); - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.lens.dragFieldToWorkspace('@timestamp'); + describe('basic drag and drop', () => { + it('should construct the basic split xy chart', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + await PageObjects.lens.goToTimeRange(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.dragFieldToWorkspace('@timestamp'); - expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_xDimensionPanel')).to.eql( - '@timestamp' - ); - }); + expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_xDimensionPanel')).to.eql( + '@timestamp' + ); + }); - it('should allow dropping fields to existing and empty dimension triggers', async () => { - await PageObjects.lens.switchToVisualization('lnsDatatable'); + it('should allow dropping fields to existing and empty dimension triggers', async () => { + await PageObjects.lens.switchToVisualization('lnsDatatable'); - await PageObjects.lens.dragFieldToDimensionTrigger( - 'clientip', - 'lnsDatatable_column > lns-dimensionTrigger' - ); - expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_column')).to.eql( - 'Top values of clientip' - ); + await PageObjects.lens.dragFieldToDimensionTrigger( + 'clientip', + 'lnsDatatable_column > lns-dimensionTrigger' + ); + expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_column')).to.eql( + 'Top values of clientip' + ); - await PageObjects.lens.dragFieldToDimensionTrigger( - 'bytes', - 'lnsDatatable_column > lns-empty-dimension' - ); - expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_column', 1)).to.eql( - 'bytes' - ); - await PageObjects.lens.dragFieldToDimensionTrigger( - '@message.raw', - 'lnsDatatable_column > lns-empty-dimension' - ); - expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_column', 2)).to.eql( - 'Top values of @message.raw' - ); - }); + await PageObjects.lens.dragFieldToDimensionTrigger( + 'bytes', + 'lnsDatatable_column > lns-empty-dimension' + ); + expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_column', 1)).to.eql( + 'bytes' + ); + await PageObjects.lens.dragFieldToDimensionTrigger( + '@message.raw', + 'lnsDatatable_column > lns-empty-dimension' + ); + expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_column', 2)).to.eql( + 'Top values of @message.raw' + ); + }); - it('should reorder the elements for the table', async () => { - await PageObjects.lens.reorderDimensions('lnsDatatable_column', 2, 0); - await PageObjects.header.waitUntilLoadingHasFinished(); - expect(await PageObjects.lens.getDimensionTriggersTexts('lnsDatatable_column')).to.eql([ - 'Top values of @message.raw', - 'Top values of clientip', - 'bytes', - ]); - }); + it('should reorder the elements for the table', async () => { + await PageObjects.lens.reorderDimensions('lnsDatatable_column', 2, 0); + await PageObjects.header.waitUntilLoadingHasFinished(); + expect(await PageObjects.lens.getDimensionTriggersTexts('lnsDatatable_column')).to.eql([ + 'Top values of @message.raw', + 'Top values of clientip', + 'bytes', + ]); + }); - it('should move the column to compatible dimension group', async () => { - await PageObjects.lens.switchToVisualization('bar'); - expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_xDimensionPanel')).to.eql([ - 'Top values of @message.raw', - ]); - expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_splitDimensionPanel')).to.eql([ - 'Top values of clientip', - ]); + it('should move the column to compatible dimension group', async () => { + await PageObjects.lens.switchToVisualization('bar'); + expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_xDimensionPanel')).to.eql([ + 'Top values of @message.raw', + ]); + expect( + await PageObjects.lens.getDimensionTriggersTexts('lnsXY_splitDimensionPanel') + ).to.eql(['Top values of clientip']); + + await PageObjects.lens.dragDimensionToDimension( + 'lnsXY_xDimensionPanel > lns-dimensionTrigger', + 'lnsXY_splitDimensionPanel > lns-dimensionTrigger' + ); + + expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_xDimensionPanel')).to.eql( + [] + ); + expect( + await PageObjects.lens.getDimensionTriggersTexts('lnsXY_splitDimensionPanel') + ).to.eql(['Top values of @message.raw']); + }); + }); - await PageObjects.lens.dragDimensionToDimension( - 'lnsXY_xDimensionPanel > lns-dimensionTrigger', - 'lnsXY_splitDimensionPanel > lns-dimensionTrigger' - ); + describe('workspace drop', () => { + it('should always nest time dimension in categorical dimension', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + await PageObjects.lens.goToTimeRange(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.dragFieldToWorkspace('@timestamp'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.dragFieldToWorkspace('clientip'); + await PageObjects.header.waitUntilLoadingHasFinished(); + expect( + await PageObjects.lens.getDimensionTriggersTexts('lnsXY_splitDimensionPanel') + ).to.eql(['Top values of clientip']); + await PageObjects.lens.openDimensionEditor( + 'lnsXY_splitDimensionPanel > lns-dimensionTrigger' + ); + expect(await PageObjects.lens.isTopLevelAggregation()).to.be(true); + await PageObjects.lens.closeDimensionEditor(); + }); - expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_xDimensionPanel')).to.eql([]); - expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_splitDimensionPanel')).to.eql([ - 'Top values of @message.raw', - ]); + it('overwrite existing time dimension if one exists already', async () => { + await PageObjects.lens.dragFieldToWorkspace('utc_time'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.dragFieldToWorkspace('clientip'); + await PageObjects.header.waitUntilLoadingHasFinished(); + expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_xDimensionPanel')).to.eql([ + 'utc_time', + ]); + }); }); }); } diff --git a/x-pack/test/functional/es_archives/lens/basic/data.json b/x-pack/test/functional/es_archives/lens/basic/data.json new file mode 100644 index 0000000000000..a985de882929d --- /dev/null +++ b/x-pack/test/functional/es_archives/lens/basic/data.json @@ -0,0 +1,577 @@ +{ + "type": "doc", + "value": { + "id": "space:default", + "index": ".kibana_1", + "source": { + "migrationVersion": { + "space": "6.6.0" + }, + "references": [], + "space": { + "_reserved": true, + "description": "This is the default space!", + "disabledFeatures": [], + "name": "Default" + }, + "type": "space" + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "index-pattern:logstash-*", + "index": ".kibana_1", + "source": { + "index-pattern": { + "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-*" + }, + "migrationVersion": { + "index-pattern": "6.5.0" + }, + "references": [], + "type": "index-pattern", + "updated_at": "2018-12-21T00:43:07.096Z" + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "index-pattern:log*", + "index": ".kibana_1", + "source": { + "index-pattern": { + "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": "log*" + }, + "migrationVersion": { + "index-pattern": "6.5.0" + }, + "references": [], + "type": "index-pattern", + "updated_at": "2018-12-21T00:43:07.096Z" + }, + "type": "_doc" + } +} + + +{ + "type": "doc", + "value": { + "id": "custom_space:index-pattern:logstash-*", + "index": ".kibana_1", + "source": { + "index-pattern": { + "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-*" + }, + "migrationVersion": { + "index-pattern": "6.5.0" + }, + "namespace": "custom_space", + "references": [], + "type": "index-pattern", + "updated_at": "2018-12-21T00:43:07.096Z" + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "visualization:i-exist", + "index": ".kibana_1", + "source": { + "migrationVersion": { + "visualization": "7.3.1" + }, + "references": [ + { + "id": "logstash-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2019-01-22T19:32:31.206Z", + "visualization": { + "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\"}}},\"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\"}}]}" + } + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "custom_space:visualization:i-exist", + "index": ".kibana_1", + "source": { + "migrationVersion": { + "visualization": "7.3.1" + }, + "namespace": "custom_space", + "references": [ + { + "id": "logstash-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2019-01-22T19:32:31.206Z", + "visualization": { + "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\"}}},\"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\"}}]}" + } + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "query:okjpgs", + "index": ".kibana_1", + "source": { + "query": { + "description": "Ok responses for jpg files", + "filters": [ + { + "$state": { + "store": "appState" + }, + "meta": { + "alias": null, + "disabled": false, + "index": "b15b1d40-a8bb-11e9-98cf-2bb06ef63e0b", + "key": "extension.raw", + "negate": false, + "params": { + "query": "jpg" + }, + "type": "phrase", + "value": "jpg" + }, + "query": { + "match": { + "extension.raw": { + "query": "jpg", + "type": "phrase" + } + } + } + } + ], + "query": { + "language": "kuery", + "query": "response:200" + }, + "title": "OKJpgs" + }, + "references": [], + "type": "query", + "updated_at": "2019-07-17T17:54:26.378Z" + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "config:8.0.0", + "index": ".kibana_1", + "source": { + "config": { + "accessibility:disableAnimations": true, + "buildNum": 9007199254740991, + "dateFormat:tz": "UTC", + "defaultIndex": "logstash-*" + }, + "references": [], + "type": "config", + "updated_at": "2019-09-04T18:47:24.761Z" + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "lens:76fc4200-cf44-11e9-b933-fd84270f3ac1", + "index": ".kibana_1", + "source": { + "lens": { + "expression": "kibana\n| kibana_context query=\"{\\\"language\\\":\\\"kuery\\\",\\\"query\\\":\\\"\\\"}\" filters=\"[]\"\n| lens_merge_tables layerIds=\"c61a8afb-a185-4fae-a064-fb3846f6c451\" \n tables={esaggs index=\"logstash-*\" metricsAtAllLevels=false partialRows=false includeFormatHints=true aggConfigs={lens_auto_date aggConfigs=\"[{\\\"id\\\":\\\"2cd09808-3915-49f4-b3b0-82767eba23f7\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"max\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"bytes\\\"}}]\"} | lens_rename_columns idMap=\"{\\\"col-0-2cd09808-3915-49f4-b3b0-82767eba23f7\\\":{\\\"dataType\\\":\\\"number\\\",\\\"isBucketed\\\":false,\\\"label\\\":\\\"Maximum of bytes\\\",\\\"operationType\\\":\\\"max\\\",\\\"scale\\\":\\\"ratio\\\",\\\"sourceField\\\":\\\"bytes\\\",\\\"id\\\":\\\"2cd09808-3915-49f4-b3b0-82767eba23f7\\\"}}\"}\n| lens_metric_chart title=\"Maximum of bytes\" accessor=\"2cd09808-3915-49f4-b3b0-82767eba23f7\" mode=\"full\"", + "state": { + "datasourceMetaData": { + "filterableIndexPatterns": [ + { + "id": "logstash-*", + "title": "logstash-*" + } + ] + }, + "datasourceStates": { + "indexpattern": { + "currentIndexPatternId": "logstash-*", + "layers": { + "c61a8afb-a185-4fae-a064-fb3846f6c451": { + "columnOrder": [ + "2cd09808-3915-49f4-b3b0-82767eba23f7" + ], + "columns": { + "2cd09808-3915-49f4-b3b0-82767eba23f7": { + "dataType": "number", + "isBucketed": false, + "label": "Maximum of bytes", + "operationType": "max", + "scale": "ratio", + "sourceField": "bytes" + } + }, + "indexPatternId": "logstash-*" + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "accessor": "2cd09808-3915-49f4-b3b0-82767eba23f7", + "isHorizontal": false, + "layerId": "c61a8afb-a185-4fae-a064-fb3846f6c451", + "layers": [ + { + "accessors": [ + "d3e62a7a-c259-4fff-a2fc-eebf20b7008a", + "26ef70a9-c837-444c-886e-6bd905ee7335" + ], + "layerId": "c61a8afb-a185-4fae-a064-fb3846f6c451", + "seriesType": "area", + "splitAccessor": "54cd64ed-2a44-4591-af84-b2624504569a", + "xAccessor": "d6e40cea-6299-43b4-9c9d-b4ee305a2ce8" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "area" + } + }, + "title": "Artistpreviouslyknownaslens", + "visualizationType": "lnsMetric" + }, + "references": [], + "type": "lens", + "updated_at": "2019-10-16T00:28:08.979Z" + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "lens:9536bed0-d57e-11ea-b169-e3a222a76b9c", + "index": ".kibana_1", + "source": { + "lens": { + "expression": "kibana\n| kibana_context query=\"{\\\"language\\\":\\\"kuery\\\",\\\"query\\\":\\\"\\\"}\" filters=\"[]\"\n| lens_merge_tables layerIds=\"4ba1a1be-6e67-434b-b3a0-f30db8ea5395\" \n tables={esaggs index=\"logstash-*\" metricsAtAllLevels=true partialRows=true includeFormatHints=true aggConfigs=\"[{\\\"id\\\":\\\"bafe3009-1776-4227-a0fe-b0d6ccbb4961\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"terms\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"geo.dest\\\",\\\"orderBy\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\",\\\"order\\\":\\\"desc\\\",\\\"size\\\":7,\\\"otherBucket\\\":false,\\\"otherBucketLabel\\\":\\\"Other\\\",\\\"missingBucket\\\":false,\\\"missingBucketLabel\\\":\\\"Missing\\\"}},{\\\"id\\\":\\\"c1ebe4c9-f283-486c-ae95-6b3e99e83bd8\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"terms\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"geo.src\\\",\\\"orderBy\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\",\\\"order\\\":\\\"desc\\\",\\\"size\\\":3,\\\"otherBucket\\\":false,\\\"otherBucketLabel\\\":\\\"Other\\\",\\\"missingBucket\\\":false,\\\"missingBucketLabel\\\":\\\"Missing\\\"}},{\\\"id\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"bytes\\\",\\\"missing\\\":0}}]\" | lens_rename_columns idMap=\"{\\\"col-0-bafe3009-1776-4227-a0fe-b0d6ccbb4961\\\":{\\\"dataType\\\":\\\"string\\\",\\\"isBucketed\\\":true,\\\"label\\\":\\\"Top values of geo.dest\\\",\\\"operationType\\\":\\\"terms\\\",\\\"params\\\":{\\\"orderBy\\\":{\\\"columnId\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\",\\\"type\\\":\\\"column\\\"},\\\"orderDirection\\\":\\\"desc\\\",\\\"size\\\":7},\\\"scale\\\":\\\"ordinal\\\",\\\"sourceField\\\":\\\"geo.dest\\\",\\\"id\\\":\\\"bafe3009-1776-4227-a0fe-b0d6ccbb4961\\\"},\\\"col-2-c1ebe4c9-f283-486c-ae95-6b3e99e83bd8\\\":{\\\"label\\\":\\\"Top values of geo.src\\\",\\\"dataType\\\":\\\"string\\\",\\\"operationType\\\":\\\"terms\\\",\\\"scale\\\":\\\"ordinal\\\",\\\"sourceField\\\":\\\"geo.src\\\",\\\"isBucketed\\\":true,\\\"params\\\":{\\\"size\\\":3,\\\"orderBy\\\":{\\\"type\\\":\\\"column\\\",\\\"columnId\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\"},\\\"orderDirection\\\":\\\"desc\\\"},\\\"id\\\":\\\"c1ebe4c9-f283-486c-ae95-6b3e99e83bd8\\\"},\\\"col-3-3dc0bd55-2087-4e60-aea2-f9910714f7db\\\":{\\\"dataType\\\":\\\"number\\\",\\\"isBucketed\\\":false,\\\"label\\\":\\\"Average of bytes\\\",\\\"operationType\\\":\\\"avg\\\",\\\"scale\\\":\\\"ratio\\\",\\\"sourceField\\\":\\\"bytes\\\",\\\"id\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\"}}\"}\n| lens_pie shape=\"pie\" hideLabels=false groups=\"bafe3009-1776-4227-a0fe-b0d6ccbb4961\"\n groups=\"c1ebe4c9-f283-486c-ae95-6b3e99e83bd8\" metric=\"3dc0bd55-2087-4e60-aea2-f9910714f7db\" numberDisplay=\"percent\" categoryDisplay=\"default\" legendDisplay=\"default\" legendPosition=\"right\" percentDecimals=3 nestedLegend=false", + "state": { + "datasourceMetaData": { + "filterableIndexPatterns": [ + { + "id": "logstash-*", + "title": "logstash-*" + } + ] + }, + "datasourceStates": { + "indexpattern": { + "currentIndexPatternId": "logstash-*", + "layers": { + "4ba1a1be-6e67-434b-b3a0-f30db8ea5395": { + "columnOrder": [ + "bafe3009-1776-4227-a0fe-b0d6ccbb4961", + "c1ebe4c9-f283-486c-ae95-6b3e99e83bd8", + "3dc0bd55-2087-4e60-aea2-f9910714f7db" + ], + "columns": { + "3dc0bd55-2087-4e60-aea2-f9910714f7db": { + "dataType": "number", + "isBucketed": false, + "label": "Average of bytes", + "operationType": "avg", + "scale": "ratio", + "sourceField": "bytes" + }, + "5bd1c078-e1dd-465b-8d25-7a6404befa88": { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": { + "interval": "auto" + }, + "scale": "interval", + "sourceField": "@timestamp" + }, + "65340cf3-8402-4494-96f2-293701c59571": { + "dataType": "number", + "isBucketed": true, + "label": "Top values of bytes", + "operationType": "terms", + "params": { + "orderBy": { + "columnId": "3dc0bd55-2087-4e60-aea2-f9910714f7db", + "type": "column" + }, + "orderDirection": "desc", + "size": 3 + }, + "scale": "ordinal", + "sourceField": "bytes" + }, + "87554e1d-3dbf-4c1c-a358-4c9d40424cfa": { + "dataType": "string", + "isBucketed": true, + "label": "Top values of type", + "operationType": "terms", + "params": { + "orderBy": { + "columnId": "3dc0bd55-2087-4e60-aea2-f9910714f7db", + "type": "column" + }, + "orderDirection": "desc", + "size": 3 + }, + "scale": "ordinal", + "sourceField": "type" + }, + "bafe3009-1776-4227-a0fe-b0d6ccbb4961": { + "dataType": "string", + "isBucketed": true, + "label": "Top values of geo.dest", + "operationType": "terms", + "params": { + "orderBy": { + "columnId": "3dc0bd55-2087-4e60-aea2-f9910714f7db", + "type": "column" + }, + "orderDirection": "desc", + "size": 7 + }, + "scale": "ordinal", + "sourceField": "geo.dest" + }, + "c1ebe4c9-f283-486c-ae95-6b3e99e83bd8": { + "dataType": "string", + "isBucketed": true, + "label": "Top values of geo.src", + "operationType": "terms", + "params": { + "orderBy": { + "columnId": "3dc0bd55-2087-4e60-aea2-f9910714f7db", + "type": "column" + }, + "orderDirection": "desc", + "size": 3 + }, + "scale": "ordinal", + "sourceField": "geo.src" + } + }, + "indexPatternId": "logstash-*" + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "layers": [ + { + "categoryDisplay": "default", + "groups": [ + "bafe3009-1776-4227-a0fe-b0d6ccbb4961", + "c1ebe4c9-f283-486c-ae95-6b3e99e83bd8" + ], + "layerId": "4ba1a1be-6e67-434b-b3a0-f30db8ea5395", + "legendDisplay": "default", + "metric": "3dc0bd55-2087-4e60-aea2-f9910714f7db", + "nestedLegend": false, + "numberDisplay": "percent" + } + ], + "shape": "pie" + } + }, + "title": "lnsPieVis", + "visualizationType": "lnsPie" + }, + "references": [], + "type": "lens", + "updated_at": "2020-08-03T11:43:43.421Z" + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "lens:76fc4200-cf44-11e9-b933-fd84270f3ac2", + "index": ".kibana_1", + "source": { + "lens": { + "expression": "kibana\n| kibana_context query=\"{\\\"query\\\":\\\"\\\",\\\"language\\\":\\\"kuery\\\"}\" filters=\"[]\"\n| lens_merge_tables layerIds=\"4ba1a1be-6e67-434b-b3a0-f30db8ea5395\" \n tables={esaggs index=\"logstash-*\" metricsAtAllLevels=false partialRows=false includeFormatHints=true aggConfigs={lens_auto_date aggConfigs=\"[{\\\"id\\\":\\\"7a5d833b-ca6f-4e48-a924-d2a28d365dc3\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"terms\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"ip\\\",\\\"orderBy\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\",\\\"order\\\":\\\"desc\\\",\\\"size\\\":3,\\\"otherBucket\\\":false,\\\"otherBucketLabel\\\":\\\"Other\\\",\\\"missingBucket\\\":false,\\\"missingBucketLabel\\\":\\\"Missing\\\"}},{\\\"id\\\":\\\"3cf18f28-3495-4d45-a55f-d97f88022099\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"date_histogram\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"@timestamp\\\",\\\"useNormalizedEsInterval\\\":true,\\\"interval\\\":\\\"auto\\\",\\\"drop_partials\\\":false,\\\"min_doc_count\\\":0,\\\"extended_bounds\\\":{}}},{\\\"id\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"bytes\\\",\\\"missing\\\":0}}]\"} | lens_rename_columns idMap=\"{\\\"col-0-7a5d833b-ca6f-4e48-a924-d2a28d365dc3\\\":{\\\"label\\\":\\\"Top values of ip\\\",\\\"dataType\\\":\\\"ip\\\",\\\"operationType\\\":\\\"terms\\\",\\\"scale\\\":\\\"ordinal\\\",\\\"suggestedPriority\\\":0,\\\"sourceField\\\":\\\"ip\\\",\\\"isBucketed\\\":true,\\\"params\\\":{\\\"size\\\":3,\\\"orderBy\\\":{\\\"type\\\":\\\"column\\\",\\\"columnId\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\"},\\\"orderDirection\\\":\\\"desc\\\"},\\\"id\\\":\\\"7a5d833b-ca6f-4e48-a924-d2a28d365dc3\\\"},\\\"col-1-3cf18f28-3495-4d45-a55f-d97f88022099\\\":{\\\"label\\\":\\\"@timestamp\\\",\\\"dataType\\\":\\\"date\\\",\\\"operationType\\\":\\\"date_histogram\\\",\\\"suggestedPriority\\\":1,\\\"sourceField\\\":\\\"@timestamp\\\",\\\"isBucketed\\\":true,\\\"scale\\\":\\\"interval\\\",\\\"params\\\":{\\\"interval\\\":\\\"auto\\\"},\\\"id\\\":\\\"3cf18f28-3495-4d45-a55f-d97f88022099\\\"},\\\"col-2-3dc0bd55-2087-4e60-aea2-f9910714f7db\\\":{\\\"label\\\":\\\"Average of bytes\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"bytes\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"id\\\":\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\"}}\"}\n| lens_xy_chart xTitle=\"@timestamp\" yTitle=\"Average of bytes\" legend={lens_xy_legendConfig isVisible=true position=\"right\"} \n layers={lens_xy_layer layerId=\"4ba1a1be-6e67-434b-b3a0-f30db8ea5395\" hide=false xAccessor=\"3cf18f28-3495-4d45-a55f-d97f88022099\" yScaleType=\"linear\" xScaleType=\"time\" isHistogram=true splitAccessor=\"7a5d833b-ca6f-4e48-a924-d2a28d365dc3\" seriesType=\"bar_stacked\" accessors=\"3dc0bd55-2087-4e60-aea2-f9910714f7db\" columnToLabel=\"{\\\"3dc0bd55-2087-4e60-aea2-f9910714f7db\\\":\\\"Average of bytes\\\",\\\"7a5d833b-ca6f-4e48-a924-d2a28d365dc3\\\":\\\"Top values of ip\\\"}\"}", + "state": { + "datasourceMetaData": { + "filterableIndexPatterns": [ + { + "id": "logstash-*", + "title": "logstash-*" + } + ] + }, + "datasourceStates": { + "indexpattern": { + "currentIndexPatternId": "logstash-*", + "layers": { + "4ba1a1be-6e67-434b-b3a0-f30db8ea5395": { + "columnOrder": [ + "7a5d833b-ca6f-4e48-a924-d2a28d365dc3", + "3cf18f28-3495-4d45-a55f-d97f88022099", + "3dc0bd55-2087-4e60-aea2-f9910714f7db" + ], + "columns": { + "3cf18f28-3495-4d45-a55f-d97f88022099": { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": { + "interval": "auto" + }, + "scale": "interval", + "sourceField": "@timestamp", + "suggestedPriority": 1 + }, + "3dc0bd55-2087-4e60-aea2-f9910714f7db": { + "dataType": "number", + "isBucketed": false, + "label": "Average of bytes", + "operationType": "avg", + "scale": "ratio", + "sourceField": "bytes" + }, + "7a5d833b-ca6f-4e48-a924-d2a28d365dc3": { + "dataType": "ip", + "isBucketed": true, + "label": "Top values of ip", + "operationType": "terms", + "params": { + "orderBy": { + "columnId": "3dc0bd55-2087-4e60-aea2-f9910714f7db", + "type": "column" + }, + "orderDirection": "desc", + "size": 3 + }, + "scale": "ordinal", + "sourceField": "ip", + "suggestedPriority": 0 + } + }, + "indexPatternId": "logstash-*" + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "layers": [ + { + "accessors": [ + "3dc0bd55-2087-4e60-aea2-f9910714f7db" + ], + "layerId": "4ba1a1be-6e67-434b-b3a0-f30db8ea5395", + "seriesType": "bar_stacked", + "splitAccessor": "7a5d833b-ca6f-4e48-a924-d2a28d365dc3", + "xAccessor": "3cf18f28-3495-4d45-a55f-d97f88022099" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "bar_stacked" + } + }, + "title": "lnsXYvis", + "visualizationType": "lnsXY" + }, + "references": [], + "type": "lens", + "updated_at": "2019-10-16T00:28:08.979Z" + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "ui-metric:DashboardPanelVersionInUrl:8.0.0", + "index": ".kibana_1", + "source": { + "type": "ui-metric", + "ui-metric": { + "count": 1 + }, + "updated_at": "2019-10-16T00:28:24.399Z" + }, + "type": "_doc" + } +} diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 04c660847bcee..31a4d6e29fc35 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -241,6 +241,9 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont }); }, + async isTopLevelAggregation() { + return await testSubjects.isEuiSwitchChecked('indexPattern-nesting-switch'); + }, /** * Removes the dimension matching a specific test subject */ From 9d7cac76f5cfb72b9a937165ba53752e1e14c0c5 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 25 Jan 2021 11:26:30 +0100 Subject: [PATCH 12/62] increase timeout on graph test for cloud (#88612) --- x-pack/test/functional/page_objects/graph_page.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/functional/page_objects/graph_page.ts b/x-pack/test/functional/page_objects/graph_page.ts index 9ce1f87b5bf3d..8dda82e4e08bb 100644 --- a/x-pack/test/functional/page_objects/graph_page.ts +++ b/x-pack/test/functional/page_objects/graph_page.ts @@ -196,7 +196,7 @@ export function GraphPageProvider({ getService, getPageObjects }: FtrProviderCon await testSubjects.click('confirmSaveSavedObjectButton'); // Confirm that the Graph has been saved. - return await testSubjects.exists('saveGraphSuccess'); + return await testSubjects.exists('saveGraphSuccess', { timeout: 10000 }); } async getSearchFilter() { From 403021fcabac0bf4416e3c2dd0735cd565068ec0 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Mon, 25 Jan 2021 11:28:10 +0100 Subject: [PATCH 13/62] [Search Sessions] omit searchSessionId from the initialState, explicitly pause refreshInterval in restoreState (#88650) --- .../lib/session_restoration.test.ts | 60 +++++++++++++++++++ .../application/lib/session_restoration.ts | 21 ++++--- .../angular/discover_state.test.ts | 29 ++++++++- .../application/angular/discover_state.ts | 15 ++--- 4 files changed, 106 insertions(+), 19 deletions(-) create mode 100644 src/plugins/dashboard/public/application/lib/session_restoration.test.ts diff --git a/src/plugins/dashboard/public/application/lib/session_restoration.test.ts b/src/plugins/dashboard/public/application/lib/session_restoration.test.ts new file mode 100644 index 0000000000000..56db5346b7c6c --- /dev/null +++ b/src/plugins/dashboard/public/application/lib/session_restoration.test.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { dataPluginMock } from '../../../../data/public/mocks'; +import { createSessionRestorationDataProvider } from './session_restoration'; +import { getAppStateDefaults } from './get_app_state_defaults'; +import { getSavedDashboardMock } from '../test_helpers'; +import { SavedObjectTagDecoratorTypeGuard } from '../../../../saved_objects_tagging_oss/public'; + +describe('createSessionRestorationDataProvider', () => { + const mockDataPlugin = dataPluginMock.createStartContract(); + const searchSessionInfoProvider = createSessionRestorationDataProvider({ + data: mockDataPlugin, + getAppState: () => + getAppStateDefaults( + getSavedDashboardMock(), + false, + ((() => false) as unknown) as SavedObjectTagDecoratorTypeGuard + ), + getDashboardTitle: () => 'Dashboard', + getDashboardId: () => 'Id', + }); + + describe('session state', () => { + test('restoreState has sessionId and initialState has not', async () => { + const searchSessionId = 'id'; + (mockDataPlugin.search.session.getSessionId as jest.Mock).mockImplementation( + () => searchSessionId + ); + const { initialState, restoreState } = await searchSessionInfoProvider.getUrlGeneratorData(); + expect(initialState.searchSessionId).toBeUndefined(); + expect(restoreState.searchSessionId).toBe(searchSessionId); + }); + + test('restoreState has absoluteTimeRange', async () => { + const relativeTime = 'relativeTime'; + const absoluteTime = 'absoluteTime'; + (mockDataPlugin.query.timefilter.timefilter.getTime as jest.Mock).mockImplementation( + () => relativeTime + ); + (mockDataPlugin.query.timefilter.timefilter.getAbsoluteTime as jest.Mock).mockImplementation( + () => absoluteTime + ); + const { initialState, restoreState } = await searchSessionInfoProvider.getUrlGeneratorData(); + expect(initialState.timeRange).toBe(relativeTime); + expect(restoreState.timeRange).toBe(absoluteTime); + }); + + test('restoreState has refreshInterval paused', async () => { + const { initialState, restoreState } = await searchSessionInfoProvider.getUrlGeneratorData(); + expect(initialState.refreshInterval).toBeUndefined(); + expect(restoreState.refreshInterval?.pause).toBe(true); + }); + }); +}); diff --git a/src/plugins/dashboard/public/application/lib/session_restoration.ts b/src/plugins/dashboard/public/application/lib/session_restoration.ts index 60a0c56a63218..fb57f8caa5ce4 100644 --- a/src/plugins/dashboard/public/application/lib/session_restoration.ts +++ b/src/plugins/dashboard/public/application/lib/session_restoration.ts @@ -21,8 +21,8 @@ export function createSessionRestorationDataProvider(deps: { getUrlGeneratorData: async () => { return { urlGeneratorId: DASHBOARD_APP_URL_GENERATOR, - initialState: getUrlGeneratorState({ ...deps, forceAbsoluteTime: false }), - restoreState: getUrlGeneratorState({ ...deps, forceAbsoluteTime: true }), + initialState: getUrlGeneratorState({ ...deps, shouldRestoreSearchSession: false }), + restoreState: getUrlGeneratorState({ ...deps, shouldRestoreSearchSession: true }), }; }, }; @@ -32,20 +32,17 @@ function getUrlGeneratorState({ data, getAppState, getDashboardId, - forceAbsoluteTime, + shouldRestoreSearchSession, }: { data: DataPublicPluginStart; getAppState: () => DashboardAppState; getDashboardId: () => string; - /** - * Can force time range from time filter to convert from relative to absolute time range - */ - forceAbsoluteTime: boolean; + shouldRestoreSearchSession: boolean; }): DashboardUrlGeneratorState { const appState = getAppState(); return { dashboardId: getDashboardId(), - timeRange: forceAbsoluteTime + timeRange: shouldRestoreSearchSession ? data.query.timefilter.timefilter.getAbsoluteTime() : data.query.timefilter.timefilter.getTime(), filters: data.query.filterManager.getFilters(), @@ -55,6 +52,12 @@ function getUrlGeneratorState({ preserveSavedFilters: false, viewMode: appState.viewMode, panels: getDashboardId() ? undefined : appState.panels, - searchSessionId: data.search.session.getSessionId(), + searchSessionId: shouldRestoreSearchSession ? data.search.session.getSessionId() : undefined, + refreshInterval: shouldRestoreSearchSession + ? { + pause: true, // force pause refresh interval when restoring a session + value: 0, + } + : undefined, }; } diff --git a/src/plugins/discover/public/application/angular/discover_state.test.ts b/src/plugins/discover/public/application/angular/discover_state.test.ts index 45e5e252e8361..809664de5f073 100644 --- a/src/plugins/discover/public/application/angular/discover_state.test.ts +++ b/src/plugins/discover/public/application/angular/discover_state.test.ts @@ -101,8 +101,9 @@ describe('Test discover state with legacy migration', () => { describe('createSearchSessionRestorationDataProvider', () => { let mockSavedSearch: SavedSearch = ({} as unknown) as SavedSearch; + const mockDataPlugin = dataPluginMock.createStartContract(); const searchSessionInfoProvider = createSearchSessionRestorationDataProvider({ - data: dataPluginMock.createStartContract(), + data: mockDataPlugin, appStateContainer: getState({ history: createBrowserHistory(), }).appStateContainer, @@ -124,4 +125,30 @@ describe('createSearchSessionRestorationDataProvider', () => { expect(await searchSessionInfoProvider.getName()).toBe('Discover'); }); }); + + describe('session state', () => { + test('restoreState has sessionId and initialState has not', async () => { + const searchSessionId = 'id'; + (mockDataPlugin.search.session.getSessionId as jest.Mock).mockImplementation( + () => searchSessionId + ); + const { initialState, restoreState } = await searchSessionInfoProvider.getUrlGeneratorData(); + expect(initialState.searchSessionId).toBeUndefined(); + expect(restoreState.searchSessionId).toBe(searchSessionId); + }); + + test('restoreState has absoluteTimeRange', async () => { + const relativeTime = 'relativeTime'; + const absoluteTime = 'absoluteTime'; + (mockDataPlugin.query.timefilter.timefilter.getTime as jest.Mock).mockImplementation( + () => relativeTime + ); + (mockDataPlugin.query.timefilter.timefilter.getAbsoluteTime as jest.Mock).mockImplementation( + () => absoluteTime + ); + const { initialState, restoreState } = await searchSessionInfoProvider.getUrlGeneratorData(); + expect(initialState.timeRange).toBe(relativeTime); + expect(restoreState.timeRange).toBe(absoluteTime); + }); + }); }); diff --git a/src/plugins/discover/public/application/angular/discover_state.ts b/src/plugins/discover/public/application/angular/discover_state.ts index fe05fceb858e5..c769e263655ab 100644 --- a/src/plugins/discover/public/application/angular/discover_state.ts +++ b/src/plugins/discover/public/application/angular/discover_state.ts @@ -275,12 +275,12 @@ export function createSearchSessionRestorationDataProvider(deps: { initialState: createUrlGeneratorState({ ...deps, getSavedSearchId, - forceAbsoluteTime: false, + shouldRestoreSearchSession: false, }), restoreState: createUrlGeneratorState({ ...deps, getSavedSearchId, - forceAbsoluteTime: true, + shouldRestoreSearchSession: true, }), }; }, @@ -291,15 +291,12 @@ function createUrlGeneratorState({ appStateContainer, data, getSavedSearchId, - forceAbsoluteTime, + shouldRestoreSearchSession, }: { appStateContainer: StateContainer; data: DataPublicPluginStart; getSavedSearchId: () => string | undefined; - /** - * Can force time range from time filter to convert from relative to absolute time range - */ - forceAbsoluteTime: boolean; + shouldRestoreSearchSession: boolean; }): DiscoverUrlGeneratorState { const appState = appStateContainer.get(); return { @@ -307,10 +304,10 @@ function createUrlGeneratorState({ indexPatternId: appState.index, query: appState.query, savedSearchId: getSavedSearchId(), - timeRange: forceAbsoluteTime + timeRange: shouldRestoreSearchSession ? data.query.timefilter.timefilter.getAbsoluteTime() : data.query.timefilter.timefilter.getTime(), - searchSessionId: data.search.session.getSessionId(), + searchSessionId: shouldRestoreSearchSession ? data.search.session.getSessionId() : undefined, columns: appState.columns, sort: appState.sort, savedQuery: appState.savedQuery, From 2ff523556d6b1cc38943369889dbbc3a5f2b6e7a Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 25 Jan 2021 12:30:49 +0200 Subject: [PATCH 14/62] [XY Axis] Fix bug on percentiles and percentiles ranks (#88576) * [XY Axis] Fix bug on percentiles and percentiles ranks * Add unit tests to renderAllSeries * make it simpler * Minor change on test * Fix license headers Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../utils/render_all_series.test.mocks.ts | 386 ++++++++++++++++++ .../public/utils/render_all_series.test.tsx | 132 ++++++ .../public/utils/render_all_series.tsx | 18 +- .../vis_type_xy/public/vis_component.tsx | 45 +- 4 files changed, 560 insertions(+), 21 deletions(-) create mode 100644 src/plugins/vis_type_xy/public/utils/render_all_series.test.mocks.ts create mode 100644 src/plugins/vis_type_xy/public/utils/render_all_series.test.tsx diff --git a/src/plugins/vis_type_xy/public/utils/render_all_series.test.mocks.ts b/src/plugins/vis_type_xy/public/utils/render_all_series.test.mocks.ts new file mode 100644 index 0000000000000..393a6ee06cf58 --- /dev/null +++ b/src/plugins/vis_type_xy/public/utils/render_all_series.test.mocks.ts @@ -0,0 +1,386 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { VisConfig } from '../types'; + +export const getVisConfig = (): VisConfig => { + return { + markSizeRatio: 5.3999999999999995, + fittingFunction: 'linear', + detailedTooltip: true, + isTimeChart: true, + showCurrentTime: false, + showValueLabel: false, + enableHistogramMode: true, + tooltip: { + type: 'vertical', + }, + aspects: { + x: { + accessor: 'col-0-2', + column: 0, + title: 'order_date per minute', + format: { + id: 'date', + params: { + pattern: 'HH:mm', + }, + }, + aggType: 'date_histogram', + aggId: '2', + params: { + date: true, + intervalESUnit: 'm', + intervalESValue: 1, + interval: 60000, + format: 'HH:mm', + }, + }, + y: [ + { + accessor: 'col-1-3', + column: 1, + title: 'Average products.base_price', + format: { + id: 'number', + }, + aggType: 'avg', + aggId: '3', + params: {}, + }, + ], + }, + xAxis: { + id: 'CategoryAxis-1', + position: 'bottom', + show: true, + style: { + axisTitle: { + visible: true, + }, + tickLabel: { + visible: true, + rotation: 0, + }, + }, + groupId: 'CategoryAxis-1', + title: 'order_date per minute', + ticks: { + show: true, + showOverlappingLabels: false, + showDuplicates: false, + }, + grid: { + show: false, + }, + scale: { + type: 'time', + }, + integersOnly: false, + }, + yAxes: [ + { + id: 'ValueAxis-1', + position: 'left', + show: true, + style: { + axisTitle: { + visible: true, + }, + tickLabel: { + visible: true, + rotation: 0, + }, + }, + groupId: 'ValueAxis-1', + title: 'Percentiles of products.base_price', + ticks: { + show: true, + rotation: 0, + showOverlappingLabels: true, + showDuplicates: true, + }, + grid: { + show: false, + }, + scale: { + mode: 'normal', + type: 'linear', + }, + domain: {}, + integersOnly: false, + }, + ], + legend: { + show: true, + position: 'right', + }, + rotation: 0, + thresholdLine: { + color: '#E7664C', + show: false, + value: 10, + width: 1, + groupId: 'ValueAxis-1', + }, + }; +}; + +export const getVisConfigPercentiles = (): VisConfig => { + return { + markSizeRatio: 5.3999999999999995, + fittingFunction: 'linear', + detailedTooltip: true, + isTimeChart: true, + showCurrentTime: false, + showValueLabel: false, + enableHistogramMode: true, + tooltip: { + type: 'vertical', + }, + aspects: { + x: { + accessor: 'col-0-2', + column: 0, + title: 'order_date per minute', + format: { + id: 'date', + params: { + pattern: 'HH:mm', + }, + }, + aggType: 'date_histogram', + aggId: '2', + params: { + date: true, + intervalESUnit: 'm', + intervalESValue: 1, + interval: 60000, + format: 'HH:mm', + }, + }, + y: [ + { + accessor: 'col-1-3.1', + column: 1, + title: '1st percentile of products.base_price', + format: { + id: 'number', + }, + aggType: 'percentiles', + aggId: '3.1', + params: {}, + }, + { + accessor: 'col-2-3.5', + column: 2, + title: '5th percentile of products.base_price', + format: { + id: 'number', + }, + aggType: 'percentiles', + aggId: '3.5', + params: {}, + }, + { + accessor: 'col-3-3.25', + column: 3, + title: '25th percentile of products.base_price', + format: { + id: 'number', + }, + aggType: 'percentiles', + aggId: '3.25', + params: {}, + }, + { + accessor: 'col-4-3.50', + column: 4, + title: '50th percentile of products.base_price', + format: { + id: 'number', + }, + aggType: 'percentiles', + aggId: '3.50', + params: {}, + }, + { + accessor: 'col-5-3.75', + column: 5, + title: '75th percentile of products.base_price', + format: { + id: 'number', + }, + aggType: 'percentiles', + aggId: '3.75', + params: {}, + }, + { + accessor: 'col-6-3.95', + column: 6, + title: '95th percentile of products.base_price', + format: { + id: 'number', + }, + aggType: 'percentiles', + aggId: '3.95', + params: {}, + }, + { + accessor: 'col-7-3.99', + column: 7, + title: '99th percentile of products.base_price', + format: { + id: 'number', + }, + aggType: 'percentiles', + aggId: '3.99', + params: {}, + }, + ], + }, + xAxis: { + id: 'CategoryAxis-1', + position: 'bottom', + show: true, + style: { + axisTitle: { + visible: true, + }, + tickLabel: { + visible: true, + rotation: 0, + }, + }, + groupId: 'CategoryAxis-1', + title: 'order_date per minute', + ticks: { + show: true, + showOverlappingLabels: false, + showDuplicates: false, + }, + grid: { + show: false, + }, + scale: { + type: 'time', + }, + integersOnly: false, + }, + yAxes: [ + { + id: 'ValueAxis-1', + position: 'left', + show: true, + style: { + axisTitle: { + visible: true, + }, + tickLabel: { + visible: true, + rotation: 0, + }, + }, + groupId: 'ValueAxis-1', + title: 'Percentiles of products.base_price', + ticks: { + show: true, + rotation: 0, + showOverlappingLabels: true, + showDuplicates: true, + }, + grid: { + show: false, + }, + scale: { + mode: 'normal', + type: 'linear', + }, + domain: {}, + integersOnly: false, + }, + ], + legend: { + show: true, + position: 'right', + }, + rotation: 0, + thresholdLine: { + color: '#E7664C', + show: false, + value: 10, + width: 1, + groupId: 'ValueAxis-1', + }, + }; +}; + +export const getPercentilesData = () => { + return [ + { + 'col-0-2': 1610961900000, + 'col-1-3.1': 11.9921875, + 'col-2-3.5': 11.9921875, + 'col-3-3.25': 11.9921875, + 'col-4-3.50': 38.49609375, + 'col-5-3.75': 65, + 'col-6-3.95': 65, + 'col-7-3.99': 65, + }, + { + 'col-0-2': 1610962980000, + 'col-1-3.1': 28.984375000000004, + 'col-2-3.5': 28.984375, + 'col-3-3.25': 28.984375, + 'col-4-3.50': 30.9921875, + 'col-5-3.75': 41.5, + 'col-6-3.95': 50, + 'col-7-3.99': 50, + }, + { + 'col-0-2': 1610963280000, + 'col-1-3.1': 11.9921875, + 'col-2-3.5': 11.9921875, + 'col-3-3.25': 11.9921875, + 'col-4-3.50': 12.9921875, + 'col-5-3.75': 13.9921875, + 'col-6-3.95': 13.9921875, + 'col-7-3.99': 13.9921875, + }, + { + 'col-0-2': 1610964180000, + 'col-1-3.1': 11.9921875, + 'col-2-3.5': 11.9921875, + 'col-3-3.25': 14.9921875, + 'col-4-3.50': 15.98828125, + 'col-5-3.75': 24.984375, + 'col-6-3.95': 85, + 'col-7-3.99': 85, + }, + { + 'col-0-2': 1610964420000, + 'col-1-3.1': 11.9921875, + 'col-2-3.5': 11.9921875, + 'col-3-3.25': 11.9921875, + 'col-4-3.50': 23.99609375, + 'col-5-3.75': 42, + 'col-6-3.95': 42, + 'col-7-3.99': 42, + }, + { + 'col-0-2': 1610964600000, + 'col-1-3.1': 10.9921875, + 'col-2-3.5': 10.992187500000002, + 'col-3-3.25': 10.9921875, + 'col-4-3.50': 12.4921875, + 'col-5-3.75': 13.9921875, + 'col-6-3.95': 13.9921875, + 'col-7-3.99': 13.9921875, + }, + ]; +}; diff --git a/src/plugins/vis_type_xy/public/utils/render_all_series.test.tsx b/src/plugins/vis_type_xy/public/utils/render_all_series.test.tsx new file mode 100644 index 0000000000000..d76ea49a2f110 --- /dev/null +++ b/src/plugins/vis_type_xy/public/utils/render_all_series.test.tsx @@ -0,0 +1,132 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; +import { AreaSeries, BarSeries, CurveType } from '@elastic/charts'; +import { DatatableRow } from '../../../expressions/public'; +import { renderAllSeries } from './render_all_series'; +import { + getVisConfig, + getVisConfigPercentiles, + getPercentilesData, +} from './render_all_series.test.mocks'; +import { SeriesParam, VisConfig } from '../types'; + +const defaultSeriesParams = [ + { + data: { + id: '3', + label: 'Label', + }, + drawLinesBetweenPoints: true, + interpolate: 'linear', + lineWidth: 2, + mode: 'stacked', + show: true, + showCircles: true, + type: 'area', + valueAxis: 'ValueAxis-1', + }, +] as SeriesParam[]; + +const defaultData = [ + { + 'col-0-2': 1610960220000, + 'col-1-3': 26.984375, + }, + { + 'col-0-2': 1610961300000, + 'col-1-3': 30.99609375, + }, + { + 'col-0-2': 1610961900000, + 'col-1-3': 38.49609375, + }, + { + 'col-0-2': 1610962980000, + 'col-1-3': 35.2421875, + }, +]; + +describe('renderAllSeries', function () { + const getAllSeries = (visConfig: VisConfig, params: SeriesParam[], data: DatatableRow[]) => { + return renderAllSeries( + visConfig, + params, + data, + jest.fn(), + jest.fn(), + 'Europe/Athens', + 'col-0-2', + [] + ); + }; + + it('renders an area Series and not a bar series if type is area', () => { + const renderSeries = getAllSeries(getVisConfig(), defaultSeriesParams, defaultData); + const wrapper = shallow(
{renderSeries}
); + expect(wrapper.find(AreaSeries).length).toBe(1); + expect(wrapper.find(BarSeries).length).toBe(0); + }); + + it('renders a bar Series in case of histogram', () => { + const barSeriesParams = [{ ...defaultSeriesParams[0], type: 'histogram' }]; + + const renderBarSeries = renderAllSeries( + getVisConfig(), + barSeriesParams as SeriesParam[], + defaultData, + jest.fn(), + jest.fn(), + 'Europe/Athens', + 'col-0-2', + [] + ); + const wrapper = shallow(
{renderBarSeries}
); + expect(wrapper.find(AreaSeries).length).toBe(0); + expect(wrapper.find(BarSeries).length).toBe(1); + }); + + it('renders the correct yAccessors for not percentile aggs', () => { + const renderSeries = getAllSeries(getVisConfig(), defaultSeriesParams, defaultData); + const wrapper = shallow(
{renderSeries}
); + expect(wrapper.find(AreaSeries).prop('yAccessors')).toEqual(['col-1-3']); + }); + + it('renders the correct yAccessors for percentile aggs', () => { + const percentilesConfig = getVisConfigPercentiles(); + const percentilesData = getPercentilesData(); + const renderPercentileSeries = renderAllSeries( + percentilesConfig, + defaultSeriesParams as SeriesParam[], + percentilesData, + jest.fn(), + jest.fn(), + 'Europe/Athens', + 'col-0-2', + [] + ); + const wrapper = shallow(
{renderPercentileSeries}
); + expect(wrapper.find(AreaSeries).prop('yAccessors')).toEqual([ + 'col-1-3.1', + 'col-2-3.5', + 'col-3-3.25', + 'col-4-3.50', + 'col-5-3.75', + 'col-6-3.95', + 'col-7-3.99', + ]); + }); + + it('defaults the CurveType to linear', () => { + const renderSeries = getAllSeries(getVisConfig(), defaultSeriesParams, defaultData); + const wrapper = shallow(
{renderSeries}
); + expect(wrapper.find(AreaSeries).prop('curve')).toEqual(CurveType.LINEAR); + }); +}); diff --git a/src/plugins/vis_type_xy/public/utils/render_all_series.tsx b/src/plugins/vis_type_xy/public/utils/render_all_series.tsx index 264fa539c1980..fb884bb235971 100644 --- a/src/plugins/vis_type_xy/public/utils/render_all_series.tsx +++ b/src/plugins/vis_type_xy/public/utils/render_all_series.tsx @@ -71,13 +71,15 @@ export const renderAllSeries = ( interpolate, type, }) => { - const yAspect = aspects.y.find(({ aggId }) => aggId === paramId); - - if (!show || !yAspect || yAspect.accessor === null) { + const yAspects = aspects.y.filter( + ({ aggId, accessor }) => aggId?.includes(paramId) && accessor !== null + ); + if (!show || !yAspects.length) { return null; } + const yAccessors = yAspects.map((aspect) => aspect.accessor) as string[]; - const id = `${type}-${yAspect.accessor}`; + const id = `${type}-${yAccessors[0]}`; const yAxisScale = yAxes.find(({ groupId: axisGroupId }) => axisGroupId === groupId)?.scale; const isStacked = mode === 'stacked' || yAxisScale?.mode === 'percentage'; const stackMode = yAxisScale?.mode === 'normal' ? undefined : yAxisScale?.mode; @@ -94,13 +96,13 @@ export const renderAllSeries = ( id={id} name={getSeriesName} color={getSeriesColor} - tickFormat={yAspect.formatter} + tickFormat={yAspects[0].formatter} groupId={pseudoGroupId} useDefaultGroupDomain={useDefaultGroupDomain} xScaleType={xAxis.scale.type} yScaleType={yAxisScale?.type} xAccessor={xAccessor} - yAccessors={[yAspect.accessor]} + yAccessors={yAccessors} splitSeriesAccessors={splitSeriesAccessors} data={data} timeZone={timeZone} @@ -125,7 +127,7 @@ export const renderAllSeries = ( id={id} fit={fittingFunction} color={getSeriesColor} - tickFormat={yAspect.formatter} + tickFormat={yAspects[0].formatter} name={getSeriesName} curve={getCurveType(interpolate)} groupId={pseudoGroupId} @@ -133,7 +135,7 @@ export const renderAllSeries = ( xScaleType={xAxis.scale.type} yScaleType={yAxisScale?.type} xAccessor={xAccessor} - yAccessors={[yAspect.accessor]} + yAccessors={yAccessors} markSizeAccessor={markSizeAccessor} markFormat={aspects.z?.formatter} splitSeriesAccessors={splitSeriesAccessors} diff --git a/src/plugins/vis_type_xy/public/vis_component.tsx b/src/plugins/vis_type_xy/public/vis_component.tsx index 0cdabd2fa409e..6f994707cbb72 100644 --- a/src/plugins/vis_type_xy/public/vis_component.tsx +++ b/src/plugins/vis_type_xy/public/vis_component.tsx @@ -296,10 +296,38 @@ const VisComponent = (props: VisComponentProps) => { ] ); const xAccessor = getXAccessor(config.aspects.x); - const splitSeriesAccessors = config.aspects.series - ? compact(config.aspects.series.map(getComplexAccessor(COMPLEX_SPLIT_ACCESSOR))) - : []; + const splitSeriesAccessors = useMemo( + () => + config.aspects.series + ? compact(config.aspects.series.map(getComplexAccessor(COMPLEX_SPLIT_ACCESSOR))) + : [], + [config.aspects.series] + ); + + const renderSeries = useMemo( + () => + renderAllSeries( + config, + visParams.seriesParams, + visData.rows, + getSeriesName, + getSeriesColor, + timeZone, + xAccessor, + splitSeriesAccessors + ), + [ + config, + getSeriesColor, + getSeriesName, + splitSeriesAccessors, + timeZone, + visData.rows, + visParams.seriesParams, + xAccessor, + ] + ); return (
{ {config.yAxes.map((axisProps) => ( ))} - {renderAllSeries( - config, - visParams.seriesParams, - visData.rows, - getSeriesName, - getSeriesColor, - timeZone, - xAccessor, - splitSeriesAccessors - )} + {renderSeries}
); From 72ef3b105a38ae66045849054adc7408daee163c Mon Sep 17 00:00:00 2001 From: Daniil Date: Mon, 25 Jan 2021 14:11:59 +0300 Subject: [PATCH 15/62] [Data table] Add telemetry for table vis split mode (#88604) * Add telemetry for table vis * Update telemetry schema * Add unit tests * Update license * Use soClient instead of esClient, update tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- src/plugins/telemetry/schema/oss_plugins.json | 30 +++++++ src/plugins/vis_type_table/common/index.ts | 9 ++ src/plugins/vis_type_table/common/types.ts | 28 ++++++ src/plugins/vis_type_table/jest.config.js | 1 + .../public/components/table_vis_options.tsx | 2 +- .../components/table_vis_options_lazy.tsx | 2 +- .../vis_type_table/public/components/utils.ts | 2 +- .../public/legacy/table_vis_legacy_fn.ts | 5 +- .../public/legacy/table_vis_legacy_type.ts | 4 +- .../vis_type_table/public/table_vis_fn.ts | 5 +- .../vis_type_table/public/table_vis_type.ts | 4 +- .../vis_type_table/public/to_ast.test.ts | 2 +- src/plugins/vis_type_table/public/to_ast.ts | 3 +- src/plugins/vis_type_table/public/types.ts | 19 +---- .../public/utils/use/use_formatted_columns.ts | 3 +- .../public/utils/use/use_pagination.ts | 2 +- src/plugins/vis_type_table/server/index.ts | 10 ++- .../server/usage_collector/get_stats.test.ts | 67 +++++++++++++++ .../server/usage_collector/get_stats.ts | 85 +++++++++++++++++++ .../server/usage_collector/index.ts | 9 ++ .../register_usage_collector.test.ts | 56 ++++++++++++ .../register_usage_collector.ts | 32 +++++++ src/plugins/vis_type_table/tsconfig.json | 1 + src/plugins/visualizations/common/index.ts | 10 +++ src/plugins/visualizations/common/types.ts | 32 +++++++ src/plugins/visualizations/public/index.ts | 5 +- .../public/legacy/vis_update_state.d.ts | 2 +- .../saved_visualization_references.test.ts | 3 +- src/plugins/visualizations/public/types.ts | 16 +--- src/plugins/visualizations/public/vis.ts | 5 +- 30 files changed, 398 insertions(+), 56 deletions(-) create mode 100644 src/plugins/vis_type_table/common/index.ts create mode 100644 src/plugins/vis_type_table/common/types.ts create mode 100644 src/plugins/vis_type_table/server/usage_collector/get_stats.test.ts create mode 100644 src/plugins/vis_type_table/server/usage_collector/get_stats.ts create mode 100644 src/plugins/vis_type_table/server/usage_collector/index.ts create mode 100644 src/plugins/vis_type_table/server/usage_collector/register_usage_collector.test.ts create mode 100644 src/plugins/vis_type_table/server/usage_collector/register_usage_collector.ts create mode 100644 src/plugins/visualizations/common/index.ts create mode 100644 src/plugins/visualizations/common/types.ts diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 50a08d96de951..27d9b5ce83203 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -5247,6 +5247,36 @@ } } }, + "vis_type_table": { + "properties": { + "total": { + "type": "long" + }, + "total_split": { + "type": "long" + }, + "split_columns": { + "properties": { + "total": { + "type": "long" + }, + "enabled": { + "type": "long" + } + } + }, + "split_rows": { + "properties": { + "total": { + "type": "long" + }, + "enabled": { + "type": "long" + } + } + } + } + }, "vis_type_vega": { "properties": { "vega_lib_specs_total": { diff --git a/src/plugins/vis_type_table/common/index.ts b/src/plugins/vis_type_table/common/index.ts new file mode 100644 index 0000000000000..cc54db82d37e7 --- /dev/null +++ b/src/plugins/vis_type_table/common/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +export * from './types'; diff --git a/src/plugins/vis_type_table/common/types.ts b/src/plugins/vis_type_table/common/types.ts new file mode 100644 index 0000000000000..3380e730770c3 --- /dev/null +++ b/src/plugins/vis_type_table/common/types.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +export const VIS_TYPE_TABLE = 'table'; + +export enum AggTypes { + SUM = 'sum', + AVG = 'avg', + MIN = 'min', + MAX = 'max', + COUNT = 'count', +} + +export interface TableVisParams { + perPage: number | ''; + showPartialRows: boolean; + showMetricsAtAllLevels: boolean; + showToolbar: boolean; + showTotal: boolean; + totalFunc: AggTypes; + percentageCol: string; + row?: boolean; +} diff --git a/src/plugins/vis_type_table/jest.config.js b/src/plugins/vis_type_table/jest.config.js index 4e5ddbcf8d7c5..3a7906f6ec543 100644 --- a/src/plugins/vis_type_table/jest.config.js +++ b/src/plugins/vis_type_table/jest.config.js @@ -11,4 +11,5 @@ module.exports = { rootDir: '../../..', roots: ['/src/plugins/vis_type_table'], testRunner: 'jasmine2', + collectCoverageFrom: ['/src/plugins/vis_type_table/**/*.{js,ts,tsx}'], }; diff --git a/src/plugins/vis_type_table/public/components/table_vis_options.tsx b/src/plugins/vis_type_table/public/components/table_vis_options.tsx index eb76659a601d6..a70ecb43f1be7 100644 --- a/src/plugins/vis_type_table/public/components/table_vis_options.tsx +++ b/src/plugins/vis_type_table/public/components/table_vis_options.tsx @@ -19,7 +19,7 @@ import { NumberInputOption, VisOptionsProps, } from '../../../vis_default_editor/public'; -import { TableVisParams } from '../types'; +import { TableVisParams } from '../../common'; import { totalAggregations } from './utils'; const { tabifyGetColumns } = search; diff --git a/src/plugins/vis_type_table/public/components/table_vis_options_lazy.tsx b/src/plugins/vis_type_table/public/components/table_vis_options_lazy.tsx index fb0044a986f5e..716b77e9c91d2 100644 --- a/src/plugins/vis_type_table/public/components/table_vis_options_lazy.tsx +++ b/src/plugins/vis_type_table/public/components/table_vis_options_lazy.tsx @@ -9,7 +9,7 @@ import React, { lazy, Suspense } from 'react'; import { EuiLoadingSpinner } from '@elastic/eui'; import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; -import { TableVisParams } from '../types'; +import { TableVisParams } from '../../common'; const TableOptionsComponent = lazy(() => import('./table_vis_options')); diff --git a/src/plugins/vis_type_table/public/components/utils.ts b/src/plugins/vis_type_table/public/components/utils.ts index f11d7bc4b7f33..8f30788c76468 100644 --- a/src/plugins/vis_type_table/public/components/utils.ts +++ b/src/plugins/vis_type_table/public/components/utils.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import { AggTypes } from '../types'; +import { AggTypes } from '../../common'; const totalAggregations = [ { diff --git a/src/plugins/vis_type_table/public/legacy/table_vis_legacy_fn.ts b/src/plugins/vis_type_table/public/legacy/table_vis_legacy_fn.ts index cec16eefb360c..db0b92154d2dd 100644 --- a/src/plugins/vis_type_table/public/legacy/table_vis_legacy_fn.ts +++ b/src/plugins/vis_type_table/public/legacy/table_vis_legacy_fn.ts @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import { ExpressionFunctionDefinition, Datatable, Render } from 'src/plugins/expressions/public'; import { tableVisLegacyResponseHandler, TableContext } from './table_vis_legacy_response_handler'; import { TableVisConfig } from '../types'; +import { VIS_TYPE_TABLE } from '../../common'; export type Input = Datatable; @@ -19,7 +20,7 @@ interface Arguments { export interface TableVisRenderValue { visData: TableContext; - visType: 'table'; + visType: typeof VIS_TYPE_TABLE; visConfig: TableVisConfig; } @@ -53,7 +54,7 @@ export const createTableVisLegacyFn = (): TableExpressionFunctionDefinition => ( as: 'table_vis', value: { visData: convertedData, - visType: 'table', + visType: VIS_TYPE_TABLE, visConfig, }, }; diff --git a/src/plugins/vis_type_table/public/legacy/table_vis_legacy_type.ts b/src/plugins/vis_type_table/public/legacy/table_vis_legacy_type.ts index a1ceee8c741d4..3e1140275593d 100644 --- a/src/plugins/vis_type_table/public/legacy/table_vis_legacy_type.ts +++ b/src/plugins/vis_type_table/public/legacy/table_vis_legacy_type.ts @@ -12,11 +12,11 @@ import { BaseVisTypeOptions } from '../../../visualizations/public'; import { TableOptions } from '../components/table_vis_options_lazy'; import { VIS_EVENT_TO_TRIGGER } from '../../../visualizations/public'; +import { TableVisParams, VIS_TYPE_TABLE } from '../../common'; import { toExpressionAst } from '../to_ast'; -import { TableVisParams } from '../types'; export const tableVisLegacyTypeDefinition: BaseVisTypeOptions = { - name: 'table', + name: VIS_TYPE_TABLE, title: i18n.translate('visTypeTable.tableVisTitle', { defaultMessage: 'Data table', }), diff --git a/src/plugins/vis_type_table/public/table_vis_fn.ts b/src/plugins/vis_type_table/public/table_vis_fn.ts index a45f1e828fc47..99fee424b8bea 100644 --- a/src/plugins/vis_type_table/public/table_vis_fn.ts +++ b/src/plugins/vis_type_table/public/table_vis_fn.ts @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import { tableVisResponseHandler, TableContext } from './table_vis_response_handler'; import { ExpressionFunctionDefinition, Datatable, Render } from '../../expressions/public'; import { TableVisConfig } from './types'; +import { VIS_TYPE_TABLE } from '../common'; export type Input = Datatable; @@ -19,7 +20,7 @@ interface Arguments { export interface TableVisRenderValue { visData: TableContext; - visType: 'table'; + visType: typeof VIS_TYPE_TABLE; visConfig: TableVisConfig; } @@ -56,7 +57,7 @@ export const createTableVisFn = (): TableExpressionFunctionDefinition => ({ as: 'table_vis', value: { visData: convertedData, - visType: 'table', + visType: VIS_TYPE_TABLE, visConfig, }, }; diff --git a/src/plugins/vis_type_table/public/table_vis_type.ts b/src/plugins/vis_type_table/public/table_vis_type.ts index 8cd45b54c6ced..ef6d85db103b3 100644 --- a/src/plugins/vis_type_table/public/table_vis_type.ts +++ b/src/plugins/vis_type_table/public/table_vis_type.ts @@ -10,13 +10,13 @@ import { i18n } from '@kbn/i18n'; import { AggGroupNames } from '../../data/public'; import { BaseVisTypeOptions } from '../../visualizations/public'; +import { TableVisParams, VIS_TYPE_TABLE } from '../common'; import { TableOptions } from './components/table_vis_options_lazy'; import { VIS_EVENT_TO_TRIGGER } from '../../../plugins/visualizations/public'; import { toExpressionAst } from './to_ast'; -import { TableVisParams } from './types'; export const tableVisTypeDefinition: BaseVisTypeOptions = { - name: 'table', + name: VIS_TYPE_TABLE, title: i18n.translate('visTypeTable.tableVisTitle', { defaultMessage: 'Data table', }), diff --git a/src/plugins/vis_type_table/public/to_ast.test.ts b/src/plugins/vis_type_table/public/to_ast.test.ts index 1ca62475b7af0..f0aed7199a2f2 100644 --- a/src/plugins/vis_type_table/public/to_ast.test.ts +++ b/src/plugins/vis_type_table/public/to_ast.test.ts @@ -8,7 +8,7 @@ import { Vis } from 'src/plugins/visualizations/public'; import { toExpressionAst } from './to_ast'; -import { AggTypes, TableVisParams } from './types'; +import { AggTypes, TableVisParams } from '../common'; const mockSchemas = { metric: [{ accessor: 1, format: { id: 'number' }, params: {}, label: 'Count', aggType: 'count' }], diff --git a/src/plugins/vis_type_table/public/to_ast.ts b/src/plugins/vis_type_table/public/to_ast.ts index 9d9f23d31d802..1cbe9832e4c98 100644 --- a/src/plugins/vis_type_table/public/to_ast.ts +++ b/src/plugins/vis_type_table/public/to_ast.ts @@ -12,8 +12,9 @@ import { } from '../../data/public'; import { buildExpression, buildExpressionFunction } from '../../expressions/public'; import { getVisSchemas, Vis, BuildPipelineParams } from '../../visualizations/public'; +import { TableVisParams } from '../common'; import { TableExpressionFunctionDefinition } from './table_vis_fn'; -import { TableVisConfig, TableVisParams } from './types'; +import { TableVisConfig } from './types'; const buildTableVisConfig = ( schemas: ReturnType, diff --git a/src/plugins/vis_type_table/public/types.ts b/src/plugins/vis_type_table/public/types.ts index 75d48f4f53ac7..03cf8bb3395d6 100644 --- a/src/plugins/vis_type_table/public/types.ts +++ b/src/plugins/vis_type_table/public/types.ts @@ -8,14 +8,7 @@ import { IFieldFormat } from 'src/plugins/data/public'; import { SchemaConfig } from 'src/plugins/visualizations/public'; - -export enum AggTypes { - SUM = 'sum', - AVG = 'avg', - MIN = 'min', - MAX = 'max', - COUNT = 'count', -} +import { TableVisParams } from '../common'; export interface Dimensions { buckets: SchemaConfig[]; @@ -44,16 +37,6 @@ export interface TableVisUseUiStateProps { setColumnsWidth: (column: ColumnWidthData) => void; } -export interface TableVisParams { - perPage: number | ''; - showPartialRows: boolean; - showMetricsAtAllLevels: boolean; - showToolbar: boolean; - showTotal: boolean; - totalFunc: AggTypes; - percentageCol: string; -} - export interface TableVisConfig extends TableVisParams { title: string; dimensions: Dimensions; diff --git a/src/plugins/vis_type_table/public/utils/use/use_formatted_columns.ts b/src/plugins/vis_type_table/public/utils/use/use_formatted_columns.ts index 5398aa908f6eb..3a733e7a9a4dc 100644 --- a/src/plugins/vis_type_table/public/utils/use/use_formatted_columns.ts +++ b/src/plugins/vis_type_table/public/utils/use/use_formatted_columns.ts @@ -9,8 +9,9 @@ import { useMemo } from 'react'; import { chain, findIndex } from 'lodash'; +import { AggTypes } from '../../../common'; import { Table } from '../../table_vis_response_handler'; -import { FormattedColumn, TableVisConfig, AggTypes } from '../../types'; +import { FormattedColumn, TableVisConfig } from '../../types'; import { getFormatService } from '../../services'; import { addPercentageColumn } from '../add_percentage_column'; diff --git a/src/plugins/vis_type_table/public/utils/use/use_pagination.ts b/src/plugins/vis_type_table/public/utils/use/use_pagination.ts index 1573a3c6b7b88..7e55e63f9249c 100644 --- a/src/plugins/vis_type_table/public/utils/use/use_pagination.ts +++ b/src/plugins/vis_type_table/public/utils/use/use_pagination.ts @@ -7,7 +7,7 @@ */ import { useCallback, useEffect, useMemo, useState } from 'react'; -import { TableVisParams } from '../../types'; +import { TableVisParams } from '../../../common'; export const usePagination = (visParams: TableVisParams, rowCount: number) => { const [pagination, setPagination] = useState({ diff --git a/src/plugins/vis_type_table/server/index.ts b/src/plugins/vis_type_table/server/index.ts index 75068c646f501..39618d687168e 100644 --- a/src/plugins/vis_type_table/server/index.ts +++ b/src/plugins/vis_type_table/server/index.ts @@ -6,9 +6,11 @@ * Public License, v 1. */ -import { PluginConfigDescriptor } from 'kibana/server'; +import { CoreSetup, PluginConfigDescriptor } from 'kibana/server'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { configSchema, ConfigSchema } from '../config'; +import { registerVisTypeTableUsageCollector } from './usage_collector'; export const config: PluginConfigDescriptor = { exposeToBrowser: { @@ -21,6 +23,10 @@ export const config: PluginConfigDescriptor = { }; export const plugin = () => ({ - setup() {}, + setup(core: CoreSetup, plugins: { usageCollection?: UsageCollectionSetup }) { + if (plugins.usageCollection) { + registerVisTypeTableUsageCollector(plugins.usageCollection); + } + }, start() {}, }); diff --git a/src/plugins/vis_type_table/server/usage_collector/get_stats.test.ts b/src/plugins/vis_type_table/server/usage_collector/get_stats.test.ts new file mode 100644 index 0000000000000..55daa5c64349a --- /dev/null +++ b/src/plugins/vis_type_table/server/usage_collector/get_stats.test.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { SavedObjectsClientContract } from 'kibana/server'; +import { getStats } from './get_stats'; + +const mockVisualizations = { + saved_objects: [ + { + attributes: { + visState: + '{"type": "table","aggs": [{ "schema": "metric" }, { "schema": "bucket" }, { "schema": "split", "enabled": true }], "params": { "row": true }}', + }, + }, + { + attributes: { + visState: + '{"type": "table","aggs": [{ "schema": "metric" }, { "schema": "bucket" }, { "schema": "split", "enabled": false }], "params": { "row": true }}', + }, + }, + { + attributes: { + visState: + '{"type": "table","aggs": [{ "schema": "metric" }, { "schema": "split", "enabled": true }], "params": { "row": false }}', + }, + }, + { + attributes: { + visState: '{"type": "table","aggs": [{ "schema": "metric" }, { "schema": "bucket" }]}', + }, + }, + { + attributes: { visState: '{"type": "histogram"}' }, + }, + ], +}; + +describe('vis_type_table getStats', () => { + const mockSoClient = ({ + find: jest.fn().mockResolvedValue(mockVisualizations), + } as unknown) as SavedObjectsClientContract; + + test('Returns stats from saved objects for table vis only', async () => { + const result = await getStats(mockSoClient); + expect(mockSoClient.find).toHaveBeenCalledWith({ + type: 'visualization', + perPage: 10000, + }); + expect(result).toEqual({ + total: 4, + total_split: 3, + split_columns: { + total: 1, + enabled: 1, + }, + split_rows: { + total: 2, + enabled: 1, + }, + }); + }); +}); diff --git a/src/plugins/vis_type_table/server/usage_collector/get_stats.ts b/src/plugins/vis_type_table/server/usage_collector/get_stats.ts new file mode 100644 index 0000000000000..bd3e1d2f089e2 --- /dev/null +++ b/src/plugins/vis_type_table/server/usage_collector/get_stats.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { ISavedObjectsRepository, SavedObjectsClientContract } from 'kibana/server'; +import { + SavedVisState, + VisualizationSavedObjectAttributes, +} from 'src/plugins/visualizations/common'; +import { TableVisParams, VIS_TYPE_TABLE } from '../../common'; + +export interface VisTypeTableUsage { + /** + * Total number of table type visualizations + */ + total: number; + /** + * Total number of table visualizations, using "Split table" agg + */ + total_split: number; + /** + * Split table by columns stats + */ + split_columns: { + total: number; + enabled: number; + }; + /** + * Split table by rows stats + */ + split_rows: { + total: number; + enabled: number; + }; +} + +/* + * Parse the response data into telemetry payload + */ +export async function getStats( + soClient: SavedObjectsClientContract | ISavedObjectsRepository +): Promise { + const visualizations = await soClient.find({ + type: 'visualization', + perPage: 10000, + }); + + const tableVisualizations = visualizations.saved_objects + .map>(({ attributes }) => JSON.parse(attributes.visState)) + .filter(({ type }) => type === VIS_TYPE_TABLE); + + const defaultStats = { + total: tableVisualizations.length, + total_split: 0, + split_columns: { + total: 0, + enabled: 0, + }, + split_rows: { + total: 0, + enabled: 0, + }, + }; + + return tableVisualizations.reduce((acc, { aggs, params }) => { + const hasSplitAgg = aggs.find((agg) => agg.schema === 'split'); + + if (hasSplitAgg) { + acc.total_split += 1; + + const isSplitRow = params.row; + const isSplitEnabled = hasSplitAgg.enabled; + + const container = isSplitRow ? acc.split_rows : acc.split_columns; + container.total += 1; + container.enabled = isSplitEnabled ? container.enabled + 1 : container.enabled; + } + + return acc; + }, defaultStats); +} diff --git a/src/plugins/vis_type_table/server/usage_collector/index.ts b/src/plugins/vis_type_table/server/usage_collector/index.ts new file mode 100644 index 0000000000000..090ed3077b27c --- /dev/null +++ b/src/plugins/vis_type_table/server/usage_collector/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +export { registerVisTypeTableUsageCollector } from './register_usage_collector'; diff --git a/src/plugins/vis_type_table/server/usage_collector/register_usage_collector.test.ts b/src/plugins/vis_type_table/server/usage_collector/register_usage_collector.test.ts new file mode 100644 index 0000000000000..cbf39a4d937a7 --- /dev/null +++ b/src/plugins/vis_type_table/server/usage_collector/register_usage_collector.test.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +jest.mock('./get_stats', () => ({ + getStats: jest.fn().mockResolvedValue({ somestat: 1 }), +})); + +import { createUsageCollectionSetupMock } from 'src/plugins/usage_collection/server/usage_collection.mock'; +import { createCollectorFetchContextMock } from 'src/plugins/usage_collection/server/mocks'; + +import { registerVisTypeTableUsageCollector } from './register_usage_collector'; +import { getStats } from './get_stats'; + +describe('registerVisTypeTableUsageCollector', () => { + it('Usage collector configs fit the shape', () => { + const mockCollectorSet = createUsageCollectionSetupMock(); + registerVisTypeTableUsageCollector(mockCollectorSet); + expect(mockCollectorSet.makeUsageCollector).toBeCalledTimes(1); + expect(mockCollectorSet.registerCollector).toBeCalledTimes(1); + expect(mockCollectorSet.makeUsageCollector).toHaveBeenCalledWith({ + type: 'vis_type_table', + isReady: expect.any(Function), + fetch: expect.any(Function), + schema: { + total: { type: 'long' }, + total_split: { type: 'long' }, + split_columns: { + total: { type: 'long' }, + enabled: { type: 'long' }, + }, + split_rows: { + total: { type: 'long' }, + enabled: { type: 'long' }, + }, + }, + }); + const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0]; + expect(usageCollectorConfig.isReady()).toBe(true); + }); + + it('Usage collector config.fetch calls getStats', async () => { + const mockCollectorSet = createUsageCollectionSetupMock(); + registerVisTypeTableUsageCollector(mockCollectorSet); + const usageCollector = mockCollectorSet.makeUsageCollector.mock.results[0].value; + const mockCollectorFetchContext = createCollectorFetchContextMock(); + const fetchResult = await usageCollector.fetch(mockCollectorFetchContext); + expect(getStats).toBeCalledTimes(1); + expect(getStats).toBeCalledWith(mockCollectorFetchContext.soClient); + expect(fetchResult).toEqual({ somestat: 1 }); + }); +}); diff --git a/src/plugins/vis_type_table/server/usage_collector/register_usage_collector.ts b/src/plugins/vis_type_table/server/usage_collector/register_usage_collector.ts new file mode 100644 index 0000000000000..2ac4ce22a47e4 --- /dev/null +++ b/src/plugins/vis_type_table/server/usage_collector/register_usage_collector.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; + +import { getStats, VisTypeTableUsage } from './get_stats'; + +export function registerVisTypeTableUsageCollector(collectorSet: UsageCollectionSetup) { + const collector = collectorSet.makeUsageCollector({ + type: 'vis_type_table', + isReady: () => true, + schema: { + total: { type: 'long' }, + total_split: { type: 'long' }, + split_columns: { + total: { type: 'long' }, + enabled: { type: 'long' }, + }, + split_rows: { + total: { type: 'long' }, + enabled: { type: 'long' }, + }, + }, + fetch: ({ soClient }) => getStats(soClient), + }); + collectorSet.registerCollector(collector); +} diff --git a/src/plugins/vis_type_table/tsconfig.json b/src/plugins/vis_type_table/tsconfig.json index bda86d06c0ff7..ccff3c349cf21 100644 --- a/src/plugins/vis_type_table/tsconfig.json +++ b/src/plugins/vis_type_table/tsconfig.json @@ -8,6 +8,7 @@ "declarationMap": true }, "include": [ + "common/**/*", "public/**/*", "server/**/*", "*.ts" diff --git a/src/plugins/visualizations/common/index.ts b/src/plugins/visualizations/common/index.ts new file mode 100644 index 0000000000000..d4133eb9b7163 --- /dev/null +++ b/src/plugins/visualizations/common/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +/** @public types */ +export * from './types'; diff --git a/src/plugins/visualizations/common/types.ts b/src/plugins/visualizations/common/types.ts new file mode 100644 index 0000000000000..4881b82a0e8d3 --- /dev/null +++ b/src/plugins/visualizations/common/types.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { SavedObjectAttributes } from 'kibana/server'; +import { AggConfigOptions } from 'src/plugins/data/common'; + +export interface VisParams { + [key: string]: any; +} + +export interface SavedVisState { + title: string; + type: string; + params: TVisParams; + aggs: AggConfigOptions[]; +} + +export interface VisualizationSavedObjectAttributes extends SavedObjectAttributes { + description: string; + kibanaSavedObjectMeta: { + searchSourceJSON: string; + }; + title: string; + version: number; + visState: string; + uiStateJSON: string; +} diff --git a/src/plugins/visualizations/public/index.ts b/src/plugins/visualizations/public/index.ts index d1976cc84acec..0bf8aa6e5c418 100644 --- a/src/plugins/visualizations/public/index.ts +++ b/src/plugins/visualizations/public/index.ts @@ -34,7 +34,7 @@ export type { Schema, ISchemas, } from './vis_types'; -export { VisParams, SerializedVis, SerializedVisData, VisData } from './vis'; +export { SerializedVis, SerializedVisData, VisData } from './vis'; export type VisualizeEmbeddableFactoryContract = PublicContract; export type VisualizeEmbeddableContract = PublicContract; export { VisualizeInput } from './embeddable'; @@ -46,12 +46,13 @@ export { PersistedState } from './persisted_state'; export { VisualizationControllerConstructor, VisualizationController, - SavedVisState, ISavedVis, VisSavedObject, VisResponseValue, VisToExpressionAst, + VisParams, } from './types'; export { ExprVisAPIEvents } from './expressions/vis'; export { VisualizationListItem, VisualizationStage } from './vis_types/vis_type_alias_registry'; export { VISUALIZE_ENABLE_LABS_SETTING } from '../common/constants'; +export { SavedVisState } from '../common'; diff --git a/src/plugins/visualizations/public/legacy/vis_update_state.d.ts b/src/plugins/visualizations/public/legacy/vis_update_state.d.ts index f3643ad6adcbe..0d871b3b1dea4 100644 --- a/src/plugins/visualizations/public/legacy/vis_update_state.d.ts +++ b/src/plugins/visualizations/public/legacy/vis_update_state.d.ts @@ -6,7 +6,7 @@ * Public License, v 1. */ -import { SavedVisState } from '../types'; +import { SavedVisState } from '../../common'; declare function updateOldState(oldState: unknown): SavedVisState; diff --git a/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references.test.ts b/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references.test.ts index c858306968ad8..a85a1d453a939 100644 --- a/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references.test.ts +++ b/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references.test.ts @@ -7,7 +7,8 @@ */ import { extractReferences, injectReferences } from './saved_visualization_references'; -import { VisSavedObject, SavedVisState } from '../types'; +import { VisSavedObject } from '../types'; +import { SavedVisState } from '../../common'; describe('extractReferences', () => { test('extracts nothing if savedSearchId is empty', () => { diff --git a/src/plugins/visualizations/public/types.ts b/src/plugins/visualizations/public/types.ts index 2e57cd00486f7..dc9ca49840561 100644 --- a/src/plugins/visualizations/public/types.ts +++ b/src/plugins/visualizations/public/types.ts @@ -7,15 +7,12 @@ */ import { SavedObject } from '../../../plugins/saved_objects/public'; -import { - AggConfigOptions, - SearchSourceFields, - TimefilterContract, -} from '../../../plugins/data/public'; +import { SearchSourceFields, TimefilterContract } from '../../../plugins/data/public'; import { ExpressionAstExpression } from '../../expressions/public'; -import { SerializedVis, Vis, VisParams } from './vis'; +import { SerializedVis, Vis } from './vis'; import { ExprVis } from './expressions/vis'; +import { SavedVisState, VisParams } from '../common/types'; export { Vis, SerializedVis, VisParams }; @@ -30,13 +27,6 @@ export type VisualizationControllerConstructor = new ( vis: ExprVis ) => VisualizationController; -export interface SavedVisState { - title: string; - type: string; - params: VisParams; - aggs: AggConfigOptions[]; -} - export interface ISavedVis { id?: string; title: string; diff --git a/src/plugins/visualizations/public/vis.ts b/src/plugins/visualizations/public/vis.ts index 58bcdb9ea49c6..56a151fb82ed3 100644 --- a/src/plugins/visualizations/public/vis.ts +++ b/src/plugins/visualizations/public/vis.ts @@ -30,6 +30,7 @@ import { AggConfigOptions, SearchSourceFields, } from '../../../plugins/data/public'; +import { VisParams } from '../common/types'; export interface SerializedVisData { expression?: string; @@ -56,10 +57,6 @@ export interface VisData { savedSearchId?: string; } -export interface VisParams { - [key: string]: any; -} - const getSearchSource = async (inputSearchSource: ISearchSource, savedSearchId?: string) => { const searchSource = inputSearchSource.createCopy(); if (savedSearchId) { From 601d9fd018ffc833d41f9a344271839b209ddb3a Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 25 Jan 2021 12:33:26 +0100 Subject: [PATCH 16/62] [Uptime] waterfall view reduce opacity for blocking/waiting color (#88611) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../step_detail/waterfall/data_formatting.test.ts | 14 +++++++------- .../step_detail/waterfall/data_formatting.ts | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts index 5c0b36874004a..3967e0404a76f 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts @@ -11,7 +11,7 @@ import { WaterfallDataEntry } from '../../waterfall/types'; describe('Palettes', () => { it('A colour palette comprising timing and mime type colours is correctly generated', () => { expect(colourPalette).toEqual({ - blocked: '#b9a888', + blocked: '#dcd4c4', connect: '#da8b45', dns: '#54b399', font: '#aa6556', @@ -173,10 +173,10 @@ describe('getSeriesAndDomain', () => { "series": Array [ Object { "config": Object { - "colour": "#b9a888", + "colour": "#dcd4c4", "showTooltip": true, "tooltipProps": Object { - "colour": "#b9a888", + "colour": "#dcd4c4", "value": "Queued / Blocked: 0.854ms", }, }, @@ -264,10 +264,10 @@ describe('getSeriesAndDomain', () => { }, Object { "config": Object { - "colour": "#b9a888", + "colour": "#dcd4c4", "showTooltip": true, "tooltipProps": Object { - "colour": "#b9a888", + "colour": "#dcd4c4", "value": "Queued / Blocked: 84.546ms", }, }, @@ -330,10 +330,10 @@ describe('getSeriesAndDomain', () => { "series": Array [ Object { "config": Object { - "colour": "#b9a888", + "colour": "#dcd4c4", "showTooltip": true, "tooltipProps": Object { - "colour": "#b9a888", + "colour": "#dcd4c4", "value": "Queued / Blocked: 0.854ms", }, }, diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts index 5e59026fd65f8..3cc0497bda8ec 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts @@ -197,7 +197,7 @@ const buildTimingPalette = (): TimingColourPalette => { const palette = Object.values(Timings).reduce>((acc, value) => { switch (value) { case Timings.Blocked: - acc[value] = SAFE_PALETTE[6]; + acc[value] = SAFE_PALETTE[16]; break; case Timings.Dns: acc[value] = SAFE_PALETTE[0]; From f15a1e685cc70844b01e3814f5d264fd1ad43586 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 25 Jan 2021 11:36:48 +0000 Subject: [PATCH 17/62] [ML] Adding jobs stats to functions shared in setup contract (#88673) * [ML] Adding jobs stats to functions shared in setup contract * updating types * adding datafeeds Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../providers/anomaly_detectors.ts | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/ml/server/shared_services/providers/anomaly_detectors.ts b/x-pack/plugins/ml/server/shared_services/providers/anomaly_detectors.ts index 3719f4d97779c..42f6eb1f8f9c8 100644 --- a/x-pack/plugins/ml/server/shared_services/providers/anomaly_detectors.ts +++ b/x-pack/plugins/ml/server/shared_services/providers/anomaly_detectors.ts @@ -5,7 +5,12 @@ */ import { KibanaRequest, SavedObjectsClientContract } from 'kibana/server'; -import { Job } from '../../../common/types/anomaly_detection_jobs'; +import { + Job, + JobStats, + Datafeed, + DatafeedStats, +} from '../../../common/types/anomaly_detection_jobs'; import { GetGuards } from '../shared_services'; export interface AnomalyDetectorsProvider { @@ -14,6 +19,9 @@ export interface AnomalyDetectorsProvider { savedObjectsClient: SavedObjectsClientContract ): { jobs(jobId?: string): Promise<{ count: number; jobs: Job[] }>; + jobStats(jobId?: string): Promise<{ count: number; jobs: JobStats[] }>; + datafeeds(datafeedId?: string): Promise<{ count: number; datafeeds: Datafeed[] }>; + datafeedStats(datafeedId?: string): Promise<{ count: number; datafeeds: DatafeedStats[] }>; }; } @@ -36,6 +44,42 @@ export function getAnomalyDetectorsProvider(getGuards: GetGuards): AnomalyDetect return body; }); }, + async jobStats(jobId?: string) { + return await getGuards(request, savedObjectsClient) + .isFullLicense() + .hasMlCapabilities(['canGetJobs']) + .ok(async ({ mlClient }) => { + const { body } = await mlClient.getJobStats<{ + count: number; + jobs: JobStats[]; + }>(jobId !== undefined ? { job_id: jobId } : undefined); + return body; + }); + }, + async datafeeds(datafeedId?: string) { + return await getGuards(request, savedObjectsClient) + .isFullLicense() + .hasMlCapabilities(['canGetDatafeeds']) + .ok(async ({ mlClient }) => { + const { body } = await mlClient.getDatafeeds<{ + count: number; + datafeeds: Datafeed[]; + }>(datafeedId !== undefined ? { datafeed_id: datafeedId } : undefined); + return body; + }); + }, + async datafeedStats(datafeedId?: string) { + return await getGuards(request, savedObjectsClient) + .isFullLicense() + .hasMlCapabilities(['canGetDatafeeds']) + .ok(async ({ mlClient }) => { + const { body } = await mlClient.getDatafeedStats<{ + count: number; + datafeeds: DatafeedStats[]; + }>(datafeedId !== undefined ? { datafeed_id: datafeedId } : undefined); + return body; + }); + }, }; }, }; From 349fd5fcade9e6c80bbe3be1b13e50fa6efbc24a Mon Sep 17 00:00:00 2001 From: Diana Derevyankina <54894989+DziyanaDzeraviankina@users.noreply.github.com> Date: Mon, 25 Jan 2021 15:26:53 +0300 Subject: [PATCH 18/62] Advanced JSON input functional test (#88609) * Advanced JSON input functional test * Replace string check with object property check Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- test/functional/apps/visualize/_inspector.ts | 31 ++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/functional/apps/visualize/_inspector.ts b/test/functional/apps/visualize/_inspector.ts index 07b5dfb8a769d..9d4623feef74a 100644 --- a/test/functional/apps/visualize/_inspector.ts +++ b/test/functional/apps/visualize/_inspector.ts @@ -6,12 +6,15 @@ * Public License, v 1. */ +import expect from '@kbn/expect'; + import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); const inspector = getService('inspector'); const filterBar = getService('filterBar'); + const testSubjects = getService('testSubjects'); const PageObjects = getPageObjects(['visualize', 'visEditor', 'visChart', 'timePicker']); describe('inspector', function describeIndexTests() { @@ -23,6 +26,34 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.timePicker.setDefaultAbsoluteRange(); }); + describe('advanced input JSON', () => { + it('should have "missing" property with value 10', async () => { + log.debug('Add Max Metric on memory field'); + await PageObjects.visEditor.clickBucket('Y-axis', 'metrics'); + await PageObjects.visEditor.selectAggregation('Max', 'metrics'); + await PageObjects.visEditor.selectField('memory', 'metrics'); + + log.debug('Add value to advanced JSON input'); + await PageObjects.visEditor.toggleAdvancedParams('2'); + await testSubjects.setValue('codeEditorContainer', '{ "missing": 10 }'); + await PageObjects.visEditor.clickGo(); + + await inspector.open(); + await inspector.openInspectorRequestsView(); + const requestTab = await inspector.getOpenRequestDetailRequestButton(); + await requestTab.click(); + const requestJSON = JSON.parse(await inspector.getCodeEditorValue()); + + expect(requestJSON.aggs['2'].max).property('missing', 10); + }); + + after(async () => { + await inspector.close(); + await PageObjects.visEditor.removeDimension(2); + await PageObjects.visEditor.clickGo(); + }); + }); + describe('inspector table', function indexPatternCreation() { it('should update table header when columns change', async function () { await inspector.open(); From 191ad14ac2ec5e275750b9d93db72c71a1d7ef98 Mon Sep 17 00:00:00 2001 From: Diana Derevyankina <54894989+DziyanaDzeraviankina@users.noreply.github.com> Date: Mon, 25 Jan 2021 15:47:03 +0300 Subject: [PATCH 19/62] Small multiples in vis_type_xy plugin (#86880) * Small multiples in vis_type_xy plugin * Fix tooltip and formatted split chart values * update advanced settings wording * Remove React import in files with no JSX and change the extension to .ts * Simplify conditions * fix bar interval on split charts in vislib * Fix charts not splitting for terms boolean fields * fix filtering for small multiples * Change tests interval values from 100 to 1000000 * Revert "Change tests interval values from 100 to 1000000" This reverts commit 92f9d1b4b9e0b1f3a6c6d1058c36c657d2c5c68f. * Fix tests for interval issue in vislib (cherry picked from commit ef45b63c47da403399f76f00b49329531d445f31) * Revert axis_scale changes related to interval * Enable _line_chart_split_chart test for new charts library * Move chart splitter id to const Co-authored-by: nickofthyme Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- docs/management/advanced-options.asciidoc | 2 +- .../static/utils/transform_click_event.ts | 62 ++++++++++++++++--- .../public/vislib/lib/axis/axis_scale.js | 10 +-- .../vis_type_xy/public/chart_splitter.tsx | 45 ++++++++++++++ .../public/components/detailed_tooltip.tsx | 34 +++++++--- .../vis_type_xy/public/components/index.ts | 1 - .../public/components/split_chart_warning.tsx | 44 ------------- .../vis_type_xy/public/config/get_aspects.ts | 7 ++- .../vis_type_xy/public/types/config.ts | 2 + .../vis_type_xy/public/utils/accessors.tsx | 15 +++-- .../vis_type_xy/public/vis_component.tsx | 40 ++++++++++-- .../vis_type_xy/public/vis_renderer.tsx | 25 +++----- .../public/vis_types/{area.tsx => area.ts} | 9 --- .../vis_types/{histogram.tsx => histogram.ts} | 9 --- .../{horizontal_bar.tsx => horizontal_bar.ts} | 9 --- .../public/vis_types/{line.tsx => line.ts} | 9 --- .../public/vis_types/split_tooltip.tsx | 20 ------ src/plugins/vis_type_xy/server/plugin.ts | 3 +- .../apps/visualize/_line_chart_split_chart.ts | 28 ++++++--- test/functional/apps/visualize/index.ts | 1 + 20 files changed, 213 insertions(+), 162 deletions(-) create mode 100644 src/plugins/vis_type_xy/public/chart_splitter.tsx delete mode 100644 src/plugins/vis_type_xy/public/components/split_chart_warning.tsx rename src/plugins/vis_type_xy/public/vis_types/{area.tsx => area.ts} (94%) rename src/plugins/vis_type_xy/public/vis_types/{histogram.tsx => histogram.ts} (94%) rename src/plugins/vis_type_xy/public/vis_types/{horizontal_bar.tsx => horizontal_bar.ts} (94%) rename src/plugins/vis_type_xy/public/vis_types/{line.tsx => line.ts} (94%) delete mode 100644 src/plugins/vis_type_xy/public/vis_types/split_tooltip.tsx diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 7e7c8953fd527..c2306b80734d8 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -458,7 +458,7 @@ of buckets to try to represent. [horizontal] [[visualization-visualize-chartslibrary]]`visualization:visualize:legacyChartsLibrary`:: -Enables legacy charts library for area, line and bar charts in visualize. Currently, only legacy charts library supports split chart aggregation. +Enables legacy charts library for area, line and bar charts in visualize. [[visualization-colormapping]]`visualization:colorMapping`:: **This setting is deprecated and will not be supported as of 8.0.** diff --git a/src/plugins/charts/public/static/utils/transform_click_event.ts b/src/plugins/charts/public/static/utils/transform_click_event.ts index 20865bea2f897..7c28db333cc83 100644 --- a/src/plugins/charts/public/static/utils/transform_click_event.ts +++ b/src/plugins/charts/public/static/utils/transform_click_event.ts @@ -30,6 +30,9 @@ export interface BrushTriggerEvent { type AllSeriesAccessors = Array<[accessor: Accessor | AccessorFn, value: string | number]>; +// TODO: replace when exported from elastic/charts +const DEFAULT_SINGLE_PANEL_SM_VALUE = '__ECH_DEFAULT_SINGLE_PANEL_SM_VALUE__'; + /** * returns accessor value from string or function accessor * @param datum @@ -82,6 +85,29 @@ const getAllSplitAccessors = ( value, ]); +/** + * Gets value from small multiple accessors + * + * Only handles single small multiple accessor + */ +function getSplitChartValue({ + smHorizontalAccessorValue, + smVerticalAccessorValue, +}: Pick): + | string + | number + | undefined { + if (smHorizontalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE) { + return smHorizontalAccessorValue; + } + + if (smVerticalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE) { + return smVerticalAccessorValue; + } + + return; +} + /** * Reduces matching column indexes * @@ -92,7 +118,8 @@ const getAllSplitAccessors = ( const columnReducer = ( xAccessor: Accessor | AccessorFn | null, yAccessor: Accessor | AccessorFn | null, - splitAccessors: AllSeriesAccessors + splitAccessors: AllSeriesAccessors, + splitChartAccessor?: Accessor | AccessorFn ) => ( acc: Array<[index: number, id: string]>, { id }: Datatable['columns'][number], @@ -101,6 +128,7 @@ const columnReducer = ( if ( (xAccessor !== null && validateAccessorId(id, xAccessor)) || (yAccessor !== null && validateAccessorId(id, yAccessor)) || + (splitChartAccessor !== undefined && validateAccessorId(id, splitChartAccessor)) || splitAccessors.some(([accessor]) => validateAccessorId(id, accessor)) ) { acc.push([index, id]); @@ -121,13 +149,18 @@ const rowFindPredicate = ( geometry: GeometryValue | null, xAccessor: Accessor | AccessorFn | null, yAccessor: Accessor | AccessorFn | null, - splitAccessors: AllSeriesAccessors + splitAccessors: AllSeriesAccessors, + splitChartAccessor?: Accessor | AccessorFn, + splitChartValue?: string | number ) => (row: Datatable['rows'][number]): boolean => (geometry === null || (xAccessor !== null && getAccessorValue(row, xAccessor) === geometry.x && yAccessor !== null && - getAccessorValue(row, yAccessor) === geometry.y)) && + getAccessorValue(row, yAccessor) === geometry.y && + (splitChartAccessor === undefined || + (splitChartValue !== undefined && + getAccessorValue(row, splitChartAccessor) === splitChartValue)))) && [...splitAccessors].every(([accessor, value]) => getAccessorValue(row, accessor) === value); /** @@ -142,19 +175,28 @@ export const getFilterFromChartClickEventFn = ( table: Datatable, xAccessor: Accessor | AccessorFn, splitSeriesAccessorFnMap?: Map, + splitChartAccessor?: Accessor | AccessorFn, negate: boolean = false ) => (points: Array<[GeometryValue, XYChartSeriesIdentifier]>): ClickTriggerEvent => { const data: ValueClickContext['data']['data'] = []; points.forEach((point) => { const [geometry, { yAccessor, splitAccessors }] = point; + const splitChartValue = getSplitChartValue(point[1]); const allSplitAccessors = getAllSplitAccessors(splitAccessors, splitSeriesAccessorFnMap); const columns = table.columns.reduce>( - columnReducer(xAccessor, yAccessor, allSplitAccessors), + columnReducer(xAccessor, yAccessor, allSplitAccessors, splitChartAccessor), [] ); const row = table.rows.findIndex( - rowFindPredicate(geometry, xAccessor, yAccessor, allSplitAccessors) + rowFindPredicate( + geometry, + xAccessor, + yAccessor, + allSplitAccessors, + splitChartAccessor, + splitChartValue + ) ); const newData = columns.map(([column, id]) => ({ table, @@ -179,16 +221,20 @@ export const getFilterFromChartClickEventFn = ( * Helper function to get filter action event from series */ export const getFilterFromSeriesFn = (table: Datatable) => ( - { splitAccessors }: XYChartSeriesIdentifier, + { splitAccessors, ...rest }: XYChartSeriesIdentifier, splitSeriesAccessorFnMap?: Map, + splitChartAccessor?: Accessor | AccessorFn, negate = false ): ClickTriggerEvent => { + const splitChartValue = getSplitChartValue(rest); const allSplitAccessors = getAllSplitAccessors(splitAccessors, splitSeriesAccessorFnMap); const columns = table.columns.reduce>( - columnReducer(null, null, allSplitAccessors), + columnReducer(null, null, allSplitAccessors, splitChartAccessor), [] ); - const row = table.rows.findIndex(rowFindPredicate(null, null, null, allSplitAccessors)); + const row = table.rows.findIndex( + rowFindPredicate(null, null, null, allSplitAccessors, splitChartAccessor, splitChartValue) + ); const data: ValueClickContext['data']['data'] = columns.map(([column, id]) => ({ table, column, diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js b/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js index 157523cdf09f4..ee9bed141fe4b 100644 --- a/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js @@ -7,7 +7,7 @@ */ import d3 from 'd3'; -import _ from 'lodash'; +import { isNumber, reduce, times } from 'lodash'; import moment from 'moment'; import { InvalidLogScaleValues } from '../../errors'; @@ -62,7 +62,7 @@ export class AxisScale { return d3[extent]( opts.reduce(function (opts, v) { - if (!_.isNumber(v)) v = +v; + if (!isNumber(v)) v = +v; if (!isNaN(v)) opts.push(v); return opts; }, []) @@ -90,7 +90,7 @@ export class AxisScale { const y = moment(x); const method = n > 0 ? 'add' : 'subtract'; - _.times(Math.abs(n), function () { + times(Math.abs(n), function () { y[method](interval); }); @@ -100,7 +100,7 @@ export class AxisScale { getAllPoints() { const config = this.axisConfig; const data = this.visConfig.data.chartData(); - const chartPoints = _.reduce( + const chartPoints = reduce( data, (chartPoints, chart, chartIndex) => { const points = chart.series.reduce((points, seri, seriIndex) => { @@ -254,6 +254,6 @@ export class AxisScale { } validateScale(scale) { - if (!scale || _.isNaN(scale)) throw new Error('scale is ' + scale); + if (!scale || Number.isNaN(scale)) throw new Error('scale is ' + scale); } } diff --git a/src/plugins/vis_type_xy/public/chart_splitter.tsx b/src/plugins/vis_type_xy/public/chart_splitter.tsx new file mode 100644 index 0000000000000..bf63ac1896bd1 --- /dev/null +++ b/src/plugins/vis_type_xy/public/chart_splitter.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React from 'react'; +import { Accessor, AccessorFn, GroupBy, GroupBySort, SmallMultiples } from '@elastic/charts'; + +interface ChartSplitterProps { + splitColumnAccessor?: Accessor | AccessorFn; + splitRowAccessor?: Accessor | AccessorFn; + sort?: GroupBySort; +} + +const CHART_SPLITTER_ID = '__chart_splitter__'; + +export const ChartSplitter = ({ + splitColumnAccessor, + splitRowAccessor, + sort, +}: ChartSplitterProps) => + splitColumnAccessor || splitRowAccessor ? ( + <> + { + const splitTypeAccessor = splitColumnAccessor || splitRowAccessor; + if (splitTypeAccessor) { + return typeof splitTypeAccessor === 'function' + ? splitTypeAccessor(datum) + : datum[splitTypeAccessor]; + } + return spec.id; + }} + sort={sort || 'dataIndex'} + /> + + + ) : null; diff --git a/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx b/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx index 49b2ab483bc55..02c7157d32c27 100644 --- a/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx +++ b/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx @@ -16,19 +16,20 @@ import { XYChartSeriesIdentifier, } from '@elastic/charts'; -import { BUCKET_TYPES } from '../../../data/public'; - import { Aspects } from '../types'; import './_detailed_tooltip.scss'; import { fillEmptyValue } from '../utils/get_series_name_fn'; -import { COMPLEX_SPLIT_ACCESSOR } from '../utils/accessors'; +import { COMPLEX_SPLIT_ACCESSOR, isRangeAggType } from '../utils/accessors'; interface TooltipData { label: string; value: string; } +// TODO: replace when exported from elastic/charts +const DEFAULT_SINGLE_PANEL_SM_VALUE = '__ECH_DEFAULT_SINGLE_PANEL_SM_VALUE__'; + const getTooltipData = ( aspects: Aspects, header: TooltipValue | null, @@ -37,10 +38,7 @@ const getTooltipData = ( const data: TooltipData[] = []; if (header) { - const xFormatter = - aspects.x.aggType === BUCKET_TYPES.DATE_RANGE || aspects.x.aggType === BUCKET_TYPES.RANGE - ? null - : aspects.x.formatter; + const xFormatter = isRangeAggType(aspects.x.aggType) ? null : aspects.x.formatter; data.push({ label: aspects.x.title, value: xFormatter ? xFormatter(header.value) : `${header.value}`, @@ -80,6 +78,28 @@ const getTooltipData = ( } }); + if ( + aspects.splitColumn && + valueSeries.smHorizontalAccessorValue !== undefined && + valueSeries.smHorizontalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE + ) { + data.push({ + label: aspects.splitColumn.title, + value: `${valueSeries.smHorizontalAccessorValue}`, + }); + } + + if ( + aspects.splitRow && + valueSeries.smVerticalAccessorValue !== undefined && + valueSeries.smVerticalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE + ) { + data.push({ + label: aspects.splitRow.title, + value: `${valueSeries.smVerticalAccessorValue}`, + }); + } + return data; }; diff --git a/src/plugins/vis_type_xy/public/components/index.ts b/src/plugins/vis_type_xy/public/components/index.ts index 260c08e0fc4a9..9b2559bafd18e 100644 --- a/src/plugins/vis_type_xy/public/components/index.ts +++ b/src/plugins/vis_type_xy/public/components/index.ts @@ -11,4 +11,3 @@ export { XYEndzones } from './xy_endzones'; export { XYCurrentTime } from './xy_current_time'; export { XYSettings } from './xy_settings'; export { XYThresholdLine } from './xy_threshold_line'; -export { SplitChartWarning } from './split_chart_warning'; diff --git a/src/plugins/vis_type_xy/public/components/split_chart_warning.tsx b/src/plugins/vis_type_xy/public/components/split_chart_warning.tsx deleted file mode 100644 index b708590e04479..0000000000000 --- a/src/plugins/vis_type_xy/public/components/split_chart_warning.tsx +++ /dev/null @@ -1,44 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import React, { FC } from 'react'; - -import { EuiLink, EuiCallOut } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; - -import { getDocLinks } from '../services'; - -export const SplitChartWarning: FC = () => { - const advancedSettingsLink = getDocLinks().links.management.visualizationSettings; - - return ( - - - - - ), - }} - /> - - ); -}; diff --git a/src/plugins/vis_type_xy/public/config/get_aspects.ts b/src/plugins/vis_type_xy/public/config/get_aspects.ts index b8da4386806d4..c031d3fa1fb9b 100644 --- a/src/plugins/vis_type_xy/public/config/get_aspects.ts +++ b/src/plugins/vis_type_xy/public/config/get_aspects.ts @@ -29,7 +29,10 @@ export function getEmptyAspect(): Aspect { }, }; } -export function getAspects(columns: DatatableColumn[], { x, y, z, series }: Dimensions): Aspects { +export function getAspects( + columns: DatatableColumn[], + { x, y, z, series, splitColumn, splitRow }: Dimensions +): Aspects { const seriesDimensions = Array.isArray(series) || series === undefined ? series : [series]; return { @@ -37,6 +40,8 @@ export function getAspects(columns: DatatableColumn[], { x, y, z, series }: Dime y: getAspectsFromDimension(columns, y) ?? [], z: z && z?.length > 0 ? getAspectsFromDimension(columns, z[0]) : undefined, series: getAspectsFromDimension(columns, seriesDimensions), + splitColumn: splitColumn?.length ? getAspectsFromDimension(columns, splitColumn[0]) : undefined, + splitRow: splitRow?.length ? getAspectsFromDimension(columns, splitRow[0]) : undefined, }; } diff --git a/src/plugins/vis_type_xy/public/types/config.ts b/src/plugins/vis_type_xy/public/types/config.ts index af3d840739f17..9d4660afa1634 100644 --- a/src/plugins/vis_type_xy/public/types/config.ts +++ b/src/plugins/vis_type_xy/public/types/config.ts @@ -43,6 +43,8 @@ export interface Aspects { y: Aspect[]; z?: Aspect; series?: Aspect[]; + splitColumn?: Aspect; + splitRow?: Aspect; } export interface AxisGrid { diff --git a/src/plugins/vis_type_xy/public/utils/accessors.tsx b/src/plugins/vis_type_xy/public/utils/accessors.tsx index d1337251d36aa..e40248ae92e12 100644 --- a/src/plugins/vis_type_xy/public/utils/accessors.tsx +++ b/src/plugins/vis_type_xy/public/utils/accessors.tsx @@ -26,11 +26,15 @@ const getFieldName = (fieldName: string, index?: number) => { return `${fieldName}${indexStr}`; }; +export const isRangeAggType = (type: string | null) => + type === BUCKET_TYPES.DATE_RANGE || type === BUCKET_TYPES.RANGE; + /** * Returns accessor function for complex accessor types * @param aspect + * @param isComplex - forces to be functional/complex accessor */ -export const getComplexAccessor = (fieldName: string) => ( +export const getComplexAccessor = (fieldName: string, isComplex: boolean = false) => ( aspect: Aspect, index?: number ): Accessor | AccessorFn | undefined => { @@ -38,12 +42,7 @@ export const getComplexAccessor = (fieldName: string) => ( return; } - if ( - !( - (aspect.aggType === BUCKET_TYPES.DATE_RANGE || aspect.aggType === BUCKET_TYPES.RANGE) && - aspect.formatter - ) - ) { + if (!((isComplex || isRangeAggType(aspect.aggType)) && aspect.formatter)) { return aspect.accessor; } @@ -51,7 +50,7 @@ export const getComplexAccessor = (fieldName: string) => ( const accessor = aspect.accessor; const fn: AccessorFn = (d) => { const v = d[accessor]; - if (!v) { + if (v === undefined) { return; } const f = formatter(v); diff --git a/src/plugins/vis_type_xy/public/vis_component.tsx b/src/plugins/vis_type_xy/public/vis_component.tsx index 6f994707cbb72..871fb408d4da0 100644 --- a/src/plugins/vis_type_xy/public/vis_component.tsx +++ b/src/plugins/vis_type_xy/public/vis_component.tsx @@ -65,6 +65,7 @@ import { getComplexAccessor, getSplitSeriesAccessorFnMap, } from './utils/accessors'; +import { ChartSplitter } from './chart_splitter'; export interface VisComponentProps { visParams: VisParams; @@ -117,7 +118,8 @@ const VisComponent = (props: VisComponentProps) => { ( visData: Datatable, xAccessor: Accessor | AccessorFn, - splitSeriesAccessors: Array + splitSeriesAccessors: Array, + splitChartAccessor?: Accessor | AccessorFn ): ElementClickListener => { const splitSeriesAccessorFnMap = getSplitSeriesAccessorFnMap(splitSeriesAccessors); return (elements) => { @@ -125,7 +127,8 @@ const VisComponent = (props: VisComponentProps) => { const event = getFilterFromChartClickEventFn( visData, xAccessor, - splitSeriesAccessorFnMap + splitSeriesAccessorFnMap, + splitChartAccessor )(elements as XYChartElementEvent[]); props.fireEvent(event); } @@ -154,12 +157,17 @@ const VisComponent = (props: VisComponentProps) => { ( visData: Datatable, xAccessor: Accessor | AccessorFn, - splitSeriesAccessors: Array + splitSeriesAccessors: Array, + splitChartAccessor?: Accessor | AccessorFn ) => { const splitSeriesAccessorFnMap = getSplitSeriesAccessorFnMap(splitSeriesAccessors); return (series: XYChartSeriesIdentifier): ClickTriggerEvent | null => { if (xAccessor !== null) { - return getFilterFromSeriesFn(visData)(series, splitSeriesAccessorFnMap); + return getFilterFromSeriesFn(visData)( + series, + splitSeriesAccessorFnMap, + splitChartAccessor + ); } return null; @@ -304,6 +312,12 @@ const VisComponent = (props: VisComponentProps) => { : [], [config.aspects.series] ); + const splitChartColumnAccessor = config.aspects.splitColumn + ? getComplexAccessor(COMPLEX_SPLIT_ACCESSOR, true)(config.aspects.splitColumn) + : undefined; + const splitChartRowAccessor = config.aspects.splitRow + ? getComplexAccessor(COMPLEX_SPLIT_ACCESSOR, true)(config.aspects.splitRow) + : undefined; const renderSeries = useMemo( () => @@ -336,6 +350,10 @@ const VisComponent = (props: VisComponentProps) => { legendPosition={legendPosition} /> + { xDomain={xDomain} adjustedXDomain={adjustedXDomain} legendColorPicker={useColorPicker(legendPosition, setColor, getSeriesName)} - onElementClick={handleFilterClick(visData, xAccessor, splitSeriesAccessors)} + onElementClick={handleFilterClick( + visData, + xAccessor, + splitSeriesAccessors, + splitChartColumnAccessor ?? splitChartRowAccessor + )} onBrushEnd={handleBrush(visData, xAccessor, 'interval' in config.aspects.x.params)} onRenderChange={onRenderChange} legendAction={ config.aspects.series && (config.aspects.series?.length ?? 0) > 0 ? getLegendActions( canFilter, - getFilterEventData(visData, xAccessor, splitSeriesAccessors), + getFilterEventData( + visData, + xAccessor, + splitSeriesAccessors, + splitChartColumnAccessor ?? splitChartRowAccessor + ), handleFilterAction, getSeriesName ) diff --git a/src/plugins/vis_type_xy/public/vis_renderer.tsx b/src/plugins/vis_type_xy/public/vis_renderer.tsx index 612388939d26b..1a47742b3d004 100644 --- a/src/plugins/vis_type_xy/public/vis_renderer.tsx +++ b/src/plugins/vis_type_xy/public/vis_renderer.tsx @@ -16,7 +16,6 @@ import { VisualizationContainer } from '../../visualizations/public'; import type { PersistedState } from '../../visualizations/public'; import { XyVisType } from '../common'; -import { SplitChartWarning } from './components/split_chart_warning'; import { VisComponentType } from './vis_component'; import { RenderValue, visName } from './xy_vis_fn'; @@ -36,24 +35,20 @@ export const xyVisRenderer: ExpressionRenderDefinition = { reuseDomNode: true, render: async (domNode, { visData, visConfig, visType, syncColors }, handlers) => { const showNoResult = shouldShowNoResultsMessage(visData, visType); - const isSplitChart = Boolean(visConfig.dimensions.splitRow); handlers.onDestroy(() => unmountComponentAtNode(domNode)); render( - <> - {isSplitChart && } - - - - + + + , domNode ); diff --git a/src/plugins/vis_type_xy/public/vis_types/area.tsx b/src/plugins/vis_type_xy/public/vis_types/area.ts similarity index 94% rename from src/plugins/vis_type_xy/public/vis_types/area.tsx rename to src/plugins/vis_type_xy/public/vis_types/area.ts index 50721c349d6e9..09007a01ca8bc 100644 --- a/src/plugins/vis_type_xy/public/vis_types/area.tsx +++ b/src/plugins/vis_type_xy/public/vis_types/area.ts @@ -6,8 +6,6 @@ * Public License, v 1. */ -import React from 'react'; - import { i18n } from '@kbn/i18n'; // @ts-ignore import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; @@ -30,7 +28,6 @@ import { toExpressionAst } from '../to_ast'; import { ChartType } from '../../common'; import { getConfigCollections } from '../editor/collections'; import { getOptionTabs } from '../editor/common_config'; -import { SplitTooltip } from './split_tooltip'; export const getAreaVisTypeDefinition = ( showElasticChartsOptions = false @@ -181,12 +178,6 @@ export const getAreaVisTypeDefinition = ( min: 0, max: 1, aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'], - // TODO: Remove when split chart aggs are supported - // https://github.com/elastic/kibana/issues/82496 - ...(showElasticChartsOptions && { - disabled: true, - tooltip: , - }), }, ], }, diff --git a/src/plugins/vis_type_xy/public/vis_types/histogram.tsx b/src/plugins/vis_type_xy/public/vis_types/histogram.ts similarity index 94% rename from src/plugins/vis_type_xy/public/vis_types/histogram.tsx rename to src/plugins/vis_type_xy/public/vis_types/histogram.ts index 4fc8dbbb80e7b..daae5f5e48e61 100644 --- a/src/plugins/vis_type_xy/public/vis_types/histogram.tsx +++ b/src/plugins/vis_type_xy/public/vis_types/histogram.ts @@ -6,8 +6,6 @@ * Public License, v 1. */ -import React from 'react'; - import { i18n } from '@kbn/i18n'; // @ts-ignore import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; @@ -30,7 +28,6 @@ import { ChartType } from '../../common'; import { getConfigCollections } from '../editor/collections'; import { getOptionTabs } from '../editor/common_config'; import { defaultCountLabel, LabelRotation } from '../../../charts/public'; -import { SplitTooltip } from './split_tooltip'; export const getHistogramVisTypeDefinition = ( showElasticChartsOptions = false @@ -184,12 +181,6 @@ export const getHistogramVisTypeDefinition = ( min: 0, max: 1, aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'], - // TODO: Remove when split chart aggs are supported - // https://github.com/elastic/kibana/issues/82496 - ...(showElasticChartsOptions && { - disabled: true, - tooltip: , - }), }, ], }, diff --git a/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.tsx b/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts similarity index 94% rename from src/plugins/vis_type_xy/public/vis_types/horizontal_bar.tsx rename to src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts index b53bb7bc9dd40..9e026fa0d7474 100644 --- a/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.tsx +++ b/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts @@ -6,8 +6,6 @@ * Public License, v 1. */ -import React from 'react'; - import { i18n } from '@kbn/i18n'; // @ts-ignore import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; @@ -30,7 +28,6 @@ import { ChartType } from '../../common'; import { getConfigCollections } from '../editor/collections'; import { getOptionTabs } from '../editor/common_config'; import { defaultCountLabel, LabelRotation } from '../../../charts/public'; -import { SplitTooltip } from './split_tooltip'; export const getHorizontalBarVisTypeDefinition = ( showElasticChartsOptions = false @@ -183,12 +180,6 @@ export const getHorizontalBarVisTypeDefinition = ( min: 0, max: 1, aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'], - // TODO: Remove when split chart aggs are supported - // https://github.com/elastic/kibana/issues/82496 - ...(showElasticChartsOptions && { - disabled: true, - tooltip: , - }), }, ], }, diff --git a/src/plugins/vis_type_xy/public/vis_types/line.tsx b/src/plugins/vis_type_xy/public/vis_types/line.ts similarity index 94% rename from src/plugins/vis_type_xy/public/vis_types/line.tsx rename to src/plugins/vis_type_xy/public/vis_types/line.ts index e9b0533b957f5..3f3087207fa19 100644 --- a/src/plugins/vis_type_xy/public/vis_types/line.tsx +++ b/src/plugins/vis_type_xy/public/vis_types/line.ts @@ -6,8 +6,6 @@ * Public License, v 1. */ -import React from 'react'; - import { i18n } from '@kbn/i18n'; // @ts-ignore import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; @@ -30,7 +28,6 @@ import { toExpressionAst } from '../to_ast'; import { ChartType } from '../../common'; import { getConfigCollections } from '../editor/collections'; import { getOptionTabs } from '../editor/common_config'; -import { SplitTooltip } from './split_tooltip'; export const getLineVisTypeDefinition = ( showElasticChartsOptions = false @@ -175,12 +172,6 @@ export const getLineVisTypeDefinition = ( min: 0, max: 1, aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'], - // TODO: Remove when split chart aggs are supported - // https://github.com/elastic/kibana/issues/82496 - ...(showElasticChartsOptions && { - disabled: true, - tooltip: , - }), }, ], }, diff --git a/src/plugins/vis_type_xy/public/vis_types/split_tooltip.tsx b/src/plugins/vis_type_xy/public/vis_types/split_tooltip.tsx deleted file mode 100644 index ca22136599341..0000000000000 --- a/src/plugins/vis_type_xy/public/vis_types/split_tooltip.tsx +++ /dev/null @@ -1,20 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import React from 'react'; - -import { FormattedMessage } from '@kbn/i18n/react'; - -export function SplitTooltip() { - return ( - - ); -} diff --git a/src/plugins/vis_type_xy/server/plugin.ts b/src/plugins/vis_type_xy/server/plugin.ts index bd7957164fd1a..fa3dddfeca02a 100644 --- a/src/plugins/vis_type_xy/server/plugin.ts +++ b/src/plugins/vis_type_xy/server/plugin.ts @@ -24,8 +24,7 @@ export const uiSettingsConfig: Record> = { description: i18n.translate( 'visTypeXy.advancedSettings.visualization.legacyChartsLibrary.description', { - defaultMessage: - 'Enables legacy charts library for area, line and bar charts in visualize. Currently, only legacy charts library supports split chart aggregation.', + defaultMessage: 'Enables legacy charts library for area, line and bar charts in visualize.', } ), category: ['visualization'], diff --git a/test/functional/apps/visualize/_line_chart_split_chart.ts b/test/functional/apps/visualize/_line_chart_split_chart.ts index aeb80a58c9655..3e74bf0b7c0ec 100644 --- a/test/functional/apps/visualize/_line_chart_split_chart.ts +++ b/test/functional/apps/visualize/_line_chart_split_chart.ts @@ -176,8 +176,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false); await PageObjects.visEditor.clickGo(); const labels = await PageObjects.visChart.getYAxisLabelsAsNumbers(); - const minLabel = 2; - const maxLabel = 5000; + const minLabel = await PageObjects.visChart.getExpectedValue(2, 1); + const maxLabel = await PageObjects.visChart.getExpectedValue(5000, 7000); const numberOfLabels = 10; expect(labels.length).to.be.greaterThan(numberOfLabels); expect(labels[0]).to.eql(minLabel); @@ -188,8 +188,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); await PageObjects.visEditor.clickGo(); const labels = await PageObjects.visChart.getYAxisLabelsAsNumbers(); - const minLabel = 2; - const maxLabel = 5000; + const minLabel = await PageObjects.visChart.getExpectedValue(2, 1); + const maxLabel = await PageObjects.visChart.getExpectedValue(5000, 7000); const numberOfLabels = 10; expect(labels.length).to.be.greaterThan(numberOfLabels); expect(labels[0]).to.eql(minLabel); @@ -201,7 +201,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false); await PageObjects.visEditor.clickGo(); const labels = await PageObjects.visChart.getYAxisLabels(); - const expectedLabels = ['0', '2,000', '4,000', '6,000', '8,000', '10,000']; + const expectedLabels = await PageObjects.visChart.getExpectedValue( + ['0', '2,000', '4,000', '6,000', '8,000', '10,000'], + ['0', '1,000', '2,000', '3,000', '4,000', '5,000', '6,000', '7,000', '8,000', '9,000'] + ); expect(labels).to.eql(expectedLabels); }); @@ -210,7 +213,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); await PageObjects.visEditor.clickGo(); const labels = await PageObjects.visChart.getYAxisLabels(); - const expectedLabels = ['2,000', '4,000', '6,000', '8,000']; + const expectedLabels = await PageObjects.visChart.getExpectedValue( + ['2,000', '4,000', '6,000', '8,000'], + ['0', '1,000', '2,000', '3,000', '4,000', '5,000', '6,000', '7,000', '8,000', '9,000'] + ); expect(labels).to.eql(expectedLabels); }); @@ -220,7 +226,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visEditor.clickGo(); const labels = await PageObjects.visChart.getYAxisLabels(); log.debug(labels); - const expectedLabels = ['0', '2,000', '4,000', '6,000', '8,000', '10,000']; + const expectedLabels = await PageObjects.visChart.getExpectedValue( + ['0', '2,000', '4,000', '6,000', '8,000', '10,000'], + ['0', '1,000', '2,000', '3,000', '4,000', '5,000', '6,000', '7,000', '8,000', '9,000'] + ); expect(labels).to.eql(expectedLabels); }); @@ -228,7 +237,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); await PageObjects.visEditor.clickGo(); const labels = await PageObjects.visChart.getYAxisLabels(); - const expectedLabels = ['2,000', '4,000', '6,000', '8,000']; + const expectedLabels = await PageObjects.visChart.getExpectedValue( + ['2,000', '4,000', '6,000', '8,000'], + ['0', '1,000', '2,000', '3,000', '4,000', '5,000', '6,000', '7,000', '8,000', '9,000'] + ); expect(labels).to.eql(expectedLabels); }); }); diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts index dddcd82f1d3f8..8dd2854419693 100644 --- a/test/functional/apps/visualize/index.ts +++ b/test/functional/apps/visualize/index.ts @@ -52,6 +52,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { // Test replaced vislib chart types loadTestFile(require.resolve('./_area_chart')); loadTestFile(require.resolve('./_line_chart_split_series')); + loadTestFile(require.resolve('./_line_chart_split_chart')); loadTestFile(require.resolve('./_point_series_options')); loadTestFile(require.resolve('./_vertical_bar_chart')); loadTestFile(require.resolve('./_vertical_bar_chart_nontimeindex')); From 16657028f1b188d959a1384a82e4557f0a168e09 Mon Sep 17 00:00:00 2001 From: Daniil Date: Mon, 25 Jan 2021 16:16:25 +0300 Subject: [PATCH 20/62] [Saved Objects] Fix saved object view path (#89057) * Fix saved object view path * Add additional check --- .../public/lib/create_field_list.ts | 3 ++- .../object_view/components/form.tsx | 5 ++-- .../object_view/saved_object_view.tsx | 18 +++++++------ .../saved_objects_edition_page.tsx | 26 +++++++++++-------- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/plugins/saved_objects_management/public/lib/create_field_list.ts b/src/plugins/saved_objects_management/public/lib/create_field_list.ts index cd30a02bd0ef3..4497fb04ffa2c 100644 --- a/src/plugins/saved_objects_management/public/lib/create_field_list.ts +++ b/src/plugins/saved_objects_management/public/lib/create_field_list.ts @@ -11,11 +11,12 @@ import { SimpleSavedObject } from '../../../../core/public'; import { castEsToKbnFieldTypeName } from '../../../data/public'; import { ObjectField } from '../management_section/types'; import { SavedObjectLoader } from '../../../saved_objects/public'; +import { SavedObjectWithMetadata } from '../types'; const maxRecursiveIterations = 20; export function createFieldList( - object: SimpleSavedObject, + object: SimpleSavedObject | SavedObjectWithMetadata, service?: SavedObjectLoader ): ObjectField[] { let fields = Object.entries(object.attributes as Record).reduce( diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx index 96a4a24f6591e..e048b92b9566c 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx @@ -19,14 +19,15 @@ import { set } from '@elastic/safer-lodash-set'; import { cloneDeep } from 'lodash'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SimpleSavedObject, SavedObjectsClientContract } from '../../../../../../core/public'; +import { SavedObjectsClientContract } from '../../../../../../core/public'; import { SavedObjectLoader } from '../../../../../saved_objects/public'; import { Field } from './field'; import { ObjectField, FieldState, SubmittedFormData } from '../../types'; import { createFieldList } from '../../../lib'; +import { SavedObjectWithMetadata } from '../../../types'; interface FormProps { - object: SimpleSavedObject; + object: SavedObjectWithMetadata; service: SavedObjectLoader; savedObjectsClient: SavedObjectsClientContract; editionEnabled: boolean; diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx index 31c0a76e16f58..3343e0a63f54c 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx @@ -14,16 +14,18 @@ import { SavedObjectsClientContract, OverlayStart, NotificationsStart, - SimpleSavedObject, ScopedHistory, + HttpSetup, } from '../../../../../core/public'; import { ISavedObjectsManagementServiceRegistry } from '../../services'; import { Header, NotFoundErrors, Intro, Form } from './components'; -import { canViewInApp } from '../../lib'; +import { canViewInApp, findObject } from '../../lib'; import { SubmittedFormData } from '../types'; +import { SavedObjectWithMetadata } from '../../types'; interface SavedObjectEditionProps { id: string; + http: HttpSetup; serviceName: string; serviceRegistry: ISavedObjectsManagementServiceRegistry; capabilities: Capabilities; @@ -36,7 +38,7 @@ interface SavedObjectEditionProps { interface SavedObjectEditionState { type: string; - object?: SimpleSavedObject; + object?: SavedObjectWithMetadata; } export class SavedObjectEdition extends Component< @@ -56,9 +58,9 @@ export class SavedObjectEdition extends Component< } componentDidMount() { - const { id, savedObjectsClient } = this.props; + const { http, id } = this.props; const { type } = this.state; - savedObjectsClient.get(type, id).then((object) => { + findObject(http, type, id).then((object) => { this.setState({ object, }); @@ -70,7 +72,7 @@ export class SavedObjectEdition extends Component< capabilities, notFoundType, serviceRegistry, - id, + http, serviceName, savedObjectsClient, } = this.props; @@ -80,7 +82,7 @@ export class SavedObjectEdition extends Component< string, boolean >; - const canView = canViewInApp(capabilities, type); + const canView = canViewInApp(capabilities, type) && Boolean(object?.meta.inAppUrl?.path); const service = serviceRegistry.get(serviceName)!.service; return ( @@ -91,7 +93,7 @@ export class SavedObjectEdition extends Component< canViewInApp={canView} type={type} onDeleteClick={() => this.delete()} - viewUrl={service.urlFor(id)} + viewUrl={http.basePath.prepend(object?.meta.inAppUrl?.path || '')} /> {notFoundType && ( <> diff --git a/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx b/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx index 758789aa0f47e..2af7c22488c51 100644 --- a/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx +++ b/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx @@ -11,6 +11,7 @@ import { useParams, useLocation } from 'react-router-dom'; import { parse } from 'query-string'; import { i18n } from '@kbn/i18n'; import { CoreStart, ChromeBreadcrumb, ScopedHistory } from 'src/core/public'; +import { RedirectAppLinks } from '../../../kibana_react/public'; import { ISavedObjectsManagementServiceRegistry } from '../services'; import { SavedObjectEdition } from './object_view'; @@ -50,17 +51,20 @@ const SavedObjectsEditionPage = ({ }, [setBreadcrumbs, service]); return ( - + + + ); }; From 50d8c69ea800f778c8530a1c56036ea590c7569d Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 25 Jan 2021 13:36:38 +0000 Subject: [PATCH 21/62] skip flaky suite (#88639) --- .../functional/tests/visualize_integration.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts b/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts index e92ba226f3959..51f4bf8883521 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts @@ -151,7 +151,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); - describe('editing', () => { + // FLAKY: https://github.com/elastic/kibana/issues/88639 + describe.skip('editing', () => { beforeEach(async () => { await PageObjects.visualize.gotoVisualizationLandingPage(); await listingTable.waitUntilTableIsLoaded(); From 99ffbbe357b5f80f10925f71e7d5862403f0a009 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Mon, 25 Jan 2021 14:46:23 +0100 Subject: [PATCH 22/62] [Lens] Restore a11y flacky tests (#88954) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/test/accessibility/apps/lens.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/test/accessibility/apps/lens.ts b/x-pack/test/accessibility/apps/lens.ts index a7cacd0ad1cbb..71673d49c0c08 100644 --- a/x-pack/test/accessibility/apps/lens.ts +++ b/x-pack/test/accessibility/apps/lens.ts @@ -7,15 +7,12 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const PageObjects = getPageObjects(['common', 'visualize', 'header', 'home', 'settings', 'lens']); + const PageObjects = getPageObjects(['common', 'visualize', 'timePicker', 'home', 'lens']); const a11y = getService('a11y'); const testSubjects = getService('testSubjects'); const listingTable = getService('listingTable'); - // FLAKY: https://github.com/elastic/kibana/issues/88926 - // FLAKY: https://github.com/elastic/kibana/issues/88927 - // FLAKY: https://github.com/elastic/kibana/issues/88929 - describe.skip('Lens', () => { + describe('Lens', () => { const lensChartName = 'MyLensChart'; before(async () => { await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', { @@ -35,12 +32,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('lens', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); + await PageObjects.timePicker.ensureHiddenNoDataPopover(); await a11y.testAppSnapshot(); }); it('lens XY chart', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); + await PageObjects.timePicker.ensureHiddenNoDataPopover(); await PageObjects.lens.configureDimension({ dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', @@ -75,6 +74,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('dimension configuration panel', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); + await PageObjects.timePicker.ensureHiddenNoDataPopover(); await PageObjects.lens.openDimensionEditor('lnsXY_xDimensionPanel > lns-empty-dimension'); await a11y.testAppSnapshot(); From a5bb86482d346c905d2946e667055cc0889d320a Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 25 Jan 2021 13:49:20 +0000 Subject: [PATCH 23/62] skip flaky suite (#89069) --- .../apps/management/search_sessions/sessions_management.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/sessions_management.ts b/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/sessions_management.ts index f06e8eba0bf68..e3797550984aa 100644 --- a/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/sessions_management.ts +++ b/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/sessions_management.ts @@ -20,7 +20,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const retry = getService('retry'); - describe('Search search sessions Management UI', () => { + // FLAKY: https://github.com/elastic/kibana/issues/89069 + describe.skip('Search search sessions Management UI', () => { describe('New search sessions', () => { before(async () => { await PageObjects.common.navigateToApp('dashboard'); From aeb6df30d5b987579fcef03805a17e3c5b7da224 Mon Sep 17 00:00:00 2001 From: Thom Heymann <190132+thomheymann@users.noreply.github.com> Date: Mon, 25 Jan 2021 13:51:57 +0000 Subject: [PATCH 24/62] Update user management page (#87133) * Update user management page * Fixed i18n errors * Fix linting errors * Add ids required for accessability * Added suggestions from code review * Fix test errors * Fix types in fleet * fix translations * Fix i18n * Added suggestions from code review * Fix i18n errors * Fix linting errors * Update messaging * Updated unit tests * Updated functional tests * Fixed functional tests * Fix linting errors * Fix React warnings * Added suggestions from code review * Added tests and renamed routes * Fix functional tests * Simplified API integration tests * Updated copy based on writing suggestions * Fixed unit tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 2 +- .../components/agent_logs/agent_logs.tsx | 2 +- x-pack/plugins/security/common/constants.ts | 13 + x-pack/plugins/security/common/model/index.ts | 2 + x-pack/plugins/security/common/model/role.ts | 22 +- .../security/public/components/breadcrumb.tsx | 137 +++ .../public/components/confirm_modal.tsx | 94 ++ .../security/public/components/doc_link.tsx | 60 ++ .../public/components/form_flyout.tsx | 97 ++ .../public/components/use_current_user.ts | 24 + .../security/public/components/use_form.ts | 205 +++++ .../security/public/components/use_html_id.ts | 27 + .../role_combo_box/role_combo_box.test.tsx | 266 +++--- .../role_combo_box/role_combo_box.tsx | 128 ++- .../role_combo_box_option.test.tsx | 57 -- .../role_combo_box/role_combo_box_option.tsx | 31 - .../roles/edit_role/edit_role_page.tsx | 2 +- .../roles/edit_role/validate_role.test.ts | 28 +- .../roles/edit_role/validate_role.ts | 22 +- .../edit_user/change_password_flyout.tsx | 286 ++++++ .../users/edit_user/confirm_delete_users.tsx | 96 ++ .../users/edit_user/confirm_disable_users.tsx | 119 +++ .../users/edit_user/confirm_enable_users.tsx | 89 ++ .../users/edit_user/create_user_page.test.tsx | 108 +++ .../users/edit_user/create_user_page.tsx | 44 + .../users/edit_user/edit_user_page.scss | 6 - .../users/edit_user/edit_user_page.test.tsx | 566 ++++++++---- .../users/edit_user/edit_user_page.tsx | 836 ++++++------------ .../management/users/edit_user/index.ts | 1 + .../management/users/edit_user/user_form.tsx | 466 ++++++++++ .../users/edit_user/validate_user.test.ts | 128 --- .../users/edit_user/validate_user.ts | 142 --- .../management/users/user_api_client.mock.ts | 2 + .../management/users/user_api_client.ts | 10 +- .../users/users_grid/users_grid_page.tsx | 2 +- .../users/users_management_app.test.tsx | 129 +-- .../management/users/users_management_app.tsx | 158 ++-- .../server/routes/users/create_or_update.ts | 7 +- .../security/server/routes/users/disable.ts | 32 + .../security/server/routes/users/enable.ts | 32 + .../security/server/routes/users/index.ts | 4 + .../translations/translations/ja-JP.json | 34 +- .../translations/translations/zh-CN.json | 34 +- x-pack/test/accessibility/apps/users.ts | 47 +- .../api_integration/apis/security/index.ts | 1 + .../apis/security/security_basic.ts | 1 + .../api_integration/apis/security/users.ts | 47 + .../dashboard_mode/dashboard_view_mode.js | 58 +- .../apps/security/doc_level_security_roles.js | 8 +- .../apps/security/field_level_security.js | 16 +- .../functional/apps/security/rbac_phase1.js | 14 +- .../functional/apps/security/role_mappings.ts | 5 +- .../apps/security/secure_roles_perm.js | 8 +- .../functional/apps/security/user_email.js | 7 +- x-pack/test/functional/apps/security/users.js | 12 +- .../functional/page_objects/security_page.ts | 108 ++- yarn.lock | 40 +- 57 files changed, 3242 insertions(+), 1680 deletions(-) create mode 100644 x-pack/plugins/security/public/components/breadcrumb.tsx create mode 100644 x-pack/plugins/security/public/components/confirm_modal.tsx create mode 100644 x-pack/plugins/security/public/components/doc_link.tsx create mode 100644 x-pack/plugins/security/public/components/form_flyout.tsx create mode 100644 x-pack/plugins/security/public/components/use_current_user.ts create mode 100644 x-pack/plugins/security/public/components/use_form.ts create mode 100644 x-pack/plugins/security/public/components/use_html_id.ts delete mode 100644 x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.test.tsx delete mode 100644 x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.tsx create mode 100644 x-pack/plugins/security/public/management/users/edit_user/change_password_flyout.tsx create mode 100644 x-pack/plugins/security/public/management/users/edit_user/confirm_delete_users.tsx create mode 100644 x-pack/plugins/security/public/management/users/edit_user/confirm_disable_users.tsx create mode 100644 x-pack/plugins/security/public/management/users/edit_user/confirm_enable_users.tsx create mode 100644 x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx create mode 100644 x-pack/plugins/security/public/management/users/edit_user/create_user_page.tsx delete mode 100644 x-pack/plugins/security/public/management/users/edit_user/edit_user_page.scss create mode 100644 x-pack/plugins/security/public/management/users/edit_user/user_form.tsx delete mode 100644 x-pack/plugins/security/public/management/users/edit_user/validate_user.test.ts delete mode 100644 x-pack/plugins/security/public/management/users/edit_user/validate_user.ts create mode 100644 x-pack/plugins/security/server/routes/users/disable.ts create mode 100644 x-pack/plugins/security/server/routes/users/enable.ts create mode 100644 x-pack/test/api_integration/apis/security/users.ts diff --git a/package.json b/package.json index 2fdc31820b9d4..24297011ccc63 100644 --- a/package.json +++ b/package.json @@ -287,7 +287,7 @@ "react-resizable": "^1.7.5", "react-router": "^5.2.0", "react-router-dom": "^5.2.0", - "react-use": "^13.27.0", + "react-use": "^15.3.4", "recompose": "^0.26.0", "redux": "^4.0.5", "redux-actions": "^2.6.5", diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx index 7326d2efb8565..8a4cf1d8566a0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx @@ -183,7 +183,7 @@ export const AgentLogsUI: React.FunctionComponent = memo(({ agen [http.basePath, state.start, state.end, logStreamQuery] ); - const [logsPanelRef, { height: logPanelHeight }] = useMeasure(); + const [logsPanelRef, { height: logPanelHeight }] = useMeasure(); const agentVersion = agent.local_metadata?.elastic?.agent?.version; const isLogFeatureAvailable = useMemo(() => { diff --git a/x-pack/plugins/security/common/constants.ts b/x-pack/plugins/security/common/constants.ts index f53b5ca6d56ca..c235c296bcbae 100644 --- a/x-pack/plugins/security/common/constants.ts +++ b/x-pack/plugins/security/common/constants.ts @@ -22,3 +22,16 @@ export const AUTH_PROVIDER_HINT_QUERY_STRING_PARAMETER = 'auth_provider_hint'; export const LOGOUT_PROVIDER_QUERY_STRING_PARAMETER = 'provider'; export const LOGOUT_REASON_QUERY_STRING_PARAMETER = 'msg'; export const NEXT_URL_QUERY_STRING_PARAMETER = 'next'; + +/** + * Matches valid usernames and role names. + * + * - Must contain only letters, numbers, spaces, punctuation and printable symbols. + * - Must not contain leading or trailing spaces. + */ +export const NAME_REGEX = /^(?! )[a-zA-Z0-9 !"#$%&'()*+,\-./\\:;<=>?@\[\]^_`{|}~]+(?) { * @param {role} the Role as returned by roles API */ export function isRoleDeprecated(role: Partial) { - return role.metadata?._deprecated ?? false; + return (role.metadata?._deprecated as boolean) ?? false; +} + +/** + * Returns whether given role is a system role or not. + * + * @param {role} the Role as returned by roles API + */ +export function isRoleSystem(role: Partial) { + return (isRoleReserved(role) && role.name?.endsWith('_system')) ?? false; +} + +/** + * Returns whether given role is an admin role or not. + * + * @param {role} the Role as returned by roles API + */ +export function isRoleAdmin(role: Partial) { + return ( + (isRoleReserved(role) && (role.name?.endsWith('_admin') || role.name === 'superuser')) ?? false + ); } /** diff --git a/x-pack/plugins/security/public/components/breadcrumb.tsx b/x-pack/plugins/security/public/components/breadcrumb.tsx new file mode 100644 index 0000000000000..7246e37b33da9 --- /dev/null +++ b/x-pack/plugins/security/public/components/breadcrumb.tsx @@ -0,0 +1,137 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { createContext, useEffect, useRef, useContext, FunctionComponent } from 'react'; +import { EuiBreadcrumb } from '@elastic/eui'; +import { useKibana } from '../../../../../src/plugins/kibana_react/public'; + +interface BreadcrumbsContext { + parents: BreadcrumbProps[]; + onMount(breadcrumbs: BreadcrumbProps[]): void; + onUnmount(breadcrumbs: BreadcrumbProps[]): void; +} + +const BreadcrumbsContext = createContext(undefined); + +export interface BreadcrumbProps extends EuiBreadcrumb { + text: string; +} + +/** + * Component that automatically sets breadcrumbs and document title based on the render tree. + * + * @example + * // Breadcrumbs will be set to: "Users > Create" + * // Document title will be set to: "Create - Users" + * + * ```typescript + * + * + * {showForm && ( + * + *
+ *
+ * )} + * + * ``` + */ +export const Breadcrumb: FunctionComponent = ({ children, ...breadcrumb }) => { + const context = useContext(BreadcrumbsContext); + const component = {children}; + + if (context) { + return component; + } + + return {component}; +}; + +export interface BreadcrumbsProviderProps { + onChange?: BreadcrumbsChangeHandler; +} + +export type BreadcrumbsChangeHandler = (breadcrumbs: BreadcrumbProps[]) => void; + +/** + * Component that can be used to define any side effects that should occur when breadcrumbs change. + * + * By default the breadcrumbs in application chrome are set and the document title is updated. + * + * @example + * ```typescript + * setBreadcrumbs(breadcrumbs)}> + * + * + * ``` + */ +export const BreadcrumbsProvider: FunctionComponent = ({ + children, + onChange, +}) => { + const { services } = useKibana(); + const breadcrumbsRef = useRef([]); + + const handleChange = (breadcrumbs: BreadcrumbProps[]) => { + if (onChange) { + onChange(breadcrumbs); + } else if (services.chrome) { + services.chrome.setBreadcrumbs(breadcrumbs); + services.chrome.docTitle.change(getDocTitle(breadcrumbs)); + } + }; + + return ( + { + if (breadcrumbs.length > breadcrumbsRef.current.length) { + breadcrumbsRef.current = breadcrumbs; + handleChange(breadcrumbs); + } + }, + onUnmount: (breadcrumbs) => { + if (breadcrumbs.length < breadcrumbsRef.current.length) { + breadcrumbsRef.current = breadcrumbs; + handleChange(breadcrumbs); + } + }, + }} + > + {children} + + ); +}; + +export interface InnerBreadcrumbProps { + breadcrumb: BreadcrumbProps; +} + +export const InnerBreadcrumb: FunctionComponent = ({ + breadcrumb, + children, +}) => { + const { parents, onMount, onUnmount } = useContext(BreadcrumbsContext)!; + const nextParents = [...parents, breadcrumb]; + + useEffect(() => { + onMount(nextParents); + return () => onUnmount(parents); + }, [breadcrumb.text, breadcrumb.href]); // eslint-disable-line react-hooks/exhaustive-deps + + return ( + + {children} + + ); +}; + +export function getDocTitle(breadcrumbs: BreadcrumbProps[], maxBreadcrumbs = 2) { + return breadcrumbs + .slice(0, maxBreadcrumbs) + .reverse() + .map(({ text }) => text); +} diff --git a/x-pack/plugins/security/public/components/confirm_modal.tsx b/x-pack/plugins/security/public/components/confirm_modal.tsx new file mode 100644 index 0000000000000..8dfbf9e3f0649 --- /dev/null +++ b/x-pack/plugins/security/public/components/confirm_modal.tsx @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; +import { + EuiButton, + EuiButtonProps, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiModalProps, + EuiOverlayMask, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export interface ConfirmModalProps extends Omit { + confirmButtonText: string; + confirmButtonColor?: EuiButtonProps['color']; + isLoading?: EuiButtonProps['isLoading']; + isDisabled?: EuiButtonProps['isDisabled']; + onCancel(): void; + onConfirm(): void; + ownFocus?: boolean; +} + +/** + * Component that renders a confirmation modal similar to `EuiConfirmModal`, except that + * it adds `isLoading` prop, which renders a loading spinner and disables action buttons, + * and `ownFocus` prop to render overlay mask. + */ +export const ConfirmModal: FunctionComponent = ({ + children, + confirmButtonColor: buttonColor, + confirmButtonText, + isLoading, + isDisabled, + onCancel, + onConfirm, + ownFocus = true, + title, + ...rest +}) => { + const modal = ( + + + {title} + + {children} + + + + + + + + + + {confirmButtonText} + + + + + + ); + + return ownFocus ? ( + {modal} + ) : ( + modal + ); +}; diff --git a/x-pack/plugins/security/public/components/doc_link.tsx b/x-pack/plugins/security/public/components/doc_link.tsx new file mode 100644 index 0000000000000..50a93b8ee5090 --- /dev/null +++ b/x-pack/plugins/security/public/components/doc_link.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useCallback, FunctionComponent } from 'react'; +import { EuiLink } from '@elastic/eui'; +import { useKibana } from '../../../../../src/plugins/kibana_react/public'; +import { CoreStart } from '../../../../../src/core/public'; + +export type DocLinks = CoreStart['docLinks']['links']; +export type GetDocLinkFunction = (app: string, doc: string) => string; + +/** + * Creates links to the documentation. + * + * @see {@link DocLink} for a component that creates a link to the docs. + * + * @example + * ```typescript + * + * Learn what privileges individual roles grant. + * + * ``` + * + * @example + * ```typescript + * const [docs] = useDocLinks(); + * + * + * Learn how to get started with dashboards. + * + * ``` + */ +export function useDocLinks(): [DocLinks, GetDocLinkFunction] { + const { services } = useKibana(); + const { links, ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = services.docLinks!; + const getDocLink = useCallback( + (app, doc) => { + return `${ELASTIC_WEBSITE_URL}guide/en/${app}/reference/${DOC_LINK_VERSION}/${doc}`; + }, + [ELASTIC_WEBSITE_URL, DOC_LINK_VERSION] + ); + return [links, getDocLink]; +} + +export interface DocLinkProps { + app: string; + doc: string; +} + +export const DocLink: FunctionComponent = ({ app, doc, children }) => { + const [, getDocLink] = useDocLinks(); + return ( + + {children} + + ); +}; diff --git a/x-pack/plugins/security/public/components/form_flyout.tsx b/x-pack/plugins/security/public/components/form_flyout.tsx new file mode 100644 index 0000000000000..a0d397f81751e --- /dev/null +++ b/x-pack/plugins/security/public/components/form_flyout.tsx @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useEffect, FunctionComponent, RefObject } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiTitle, + EuiFlyout, + EuiFlyoutProps, + EuiFlyoutHeader, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiButtonProps, + EuiButtonEmpty, + EuiPortal, +} from '@elastic/eui'; +import { useHtmlId } from './use_html_id'; + +export interface FormFlyoutProps extends Omit { + title: string; + initialFocus?: RefObject; + onCancel(): void; + onSubmit(): void; + submitButtonText: string; + submitButtonColor?: EuiButtonProps['color']; + isLoading?: EuiButtonProps['isLoading']; + isDisabled?: EuiButtonProps['isDisabled']; +} + +export const FormFlyout: FunctionComponent = ({ + title, + submitButtonText, + submitButtonColor, + onCancel, + onSubmit, + isLoading, + isDisabled, + children, + initialFocus, + ...rest +}) => { + useEffect(() => { + if (initialFocus && initialFocus.current) { + initialFocus.current.focus(); + } + }, [initialFocus]); + + const titleId = useHtmlId('formFlyout', 'title'); + + return ( + + + + +

{title}

+
+
+ {children} + + + + + + + + + + {submitButtonText} + + + + +
+
+ ); +}; diff --git a/x-pack/plugins/security/public/components/use_current_user.ts b/x-pack/plugins/security/public/components/use_current_user.ts new file mode 100644 index 0000000000000..b686e0ae9d778 --- /dev/null +++ b/x-pack/plugins/security/public/components/use_current_user.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import useAsync from 'react-use/lib/useAsync'; +import constate from 'constate'; +import { AuthenticationServiceSetup } from '../authentication'; + +export interface AuthenticationProviderProps { + authc: AuthenticationServiceSetup; +} + +const [AuthenticationProvider, useAuthentication] = constate( + ({ authc }: AuthenticationProviderProps) => authc +); + +export { AuthenticationProvider, useAuthentication }; + +export function useCurrentUser() { + const authc = useAuthentication(); + return useAsync(authc.getCurrentUser, [authc]); +} diff --git a/x-pack/plugins/security/public/components/use_form.ts b/x-pack/plugins/security/public/components/use_form.ts new file mode 100644 index 0000000000000..33c7e184ec171 --- /dev/null +++ b/x-pack/plugins/security/public/components/use_form.ts @@ -0,0 +1,205 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ChangeEventHandler, FocusEventHandler, ReactEventHandler, useState } from 'react'; +import { get, set, cloneDeep, cloneDeepWith } from 'lodash'; +import useAsyncFn from 'react-use/lib/useAsyncFn'; + +export type FormReturnTuple = [FormState, FormProps]; + +export interface FormProps { + onSubmit: ReactEventHandler; + onChange: ChangeEventHandler; + onBlur: FocusEventHandler; +} + +export interface FormOptions { + onSubmit: SubmitCallback; + validate: ValidateCallback; + defaultValues: Values; +} + +/** + * Returns state and {@link HTMLFormElement} event handlers useful for creating + * forms with inline validation. + * + * @see {@link useFormState} if you don't want to use {@link HTMLFormElement}. + * + * @example + * ```typescript + * const [form, eventHandlers] = useForm({ + * onSubmit: (values) => apiClient.create(values), + * validate: (values) => !values.email ? { email: 'Required' } : {} + * }); + * + * + * + * Submit + * + * ``` + */ +export function useForm( + options: FormOptions +): FormReturnTuple { + const form = useFormState(options); + + const eventHandlers: FormProps = { + onSubmit: (event) => { + event.preventDefault(); + form.submit(); + }, + onChange: (event) => { + const { name, type, checked, value } = event.target; + if (name) { + form.setValue(name, type === 'checkbox' ? checked : value); + } + }, + onBlur: (event) => { + const { name } = event.target; + if (name) { + form.setTouched(event.target.name); + } + }, + }; + + return [form, eventHandlers]; +} + +export type FormValues = Record; +export type SubmitCallback = (values: Values) => Promise; +export type ValidateCallback = ( + values: Values +) => ValidationErrors | Promise>; +export type ValidationErrors = DeepMap; +export type TouchedFields = DeepMap; + +export interface FormState { + setValue(name: string, value: any): Promise; + setError(name: string, message: string): void; + setTouched(name: string): Promise; + reset(values: Values): void; + submit(): Promise; + values: Values; + errors: ValidationErrors; + touched: TouchedFields; + isValidating: boolean; + isSubmitting: boolean; + submitError: Error | undefined; + isInvalid: boolean; + isSubmitted: boolean; +} + +/** + * Returns state useful for creating forms with inline validation. + * + * @example + * ```typescript + * const form = useFormState({ + * onSubmit: (values) => apiClient.create(values), + * validate: (values) => !values.toggle ? { toggle: 'Required' } : {} + * }); + * + * form.setValue('toggle', e.target.checked)} + * onBlur={() => form.setTouched('toggle')} + * isInvalid={!!form.errors.toggle} + * /> + * + * Submit + * + * ``` + */ +export function useFormState({ + onSubmit, + validate, + defaultValues, +}: FormOptions): FormState { + const [values, setValues] = useState(defaultValues); + const [errors, setErrors] = useState>({}); + const [touched, setTouched] = useState>({}); + const [submitCount, setSubmitCount] = useState(0); + + const [validationState, validateForm] = useAsyncFn( + async (formValues: Values) => { + const nextErrors = await validate(formValues); + setErrors(nextErrors); + if (Object.keys(nextErrors).length === 0) { + setSubmitCount(0); + } + return nextErrors; + }, + [validate] + ); + + const [submitState, submitForm] = useAsyncFn( + async (formValues: Values) => { + const nextErrors = await validateForm(formValues); + setTouched(mapDeep(formValues, true)); + setSubmitCount(submitCount + 1); + if (Object.keys(nextErrors).length === 0) { + return onSubmit(formValues); + } + }, + [validateForm, onSubmit] + ); + + return { + setValue: async (name, value) => { + const nextValues = setDeep(values, name, value); + setValues(nextValues); + await validateForm(nextValues); + }, + setTouched: async (name, value = true) => { + setTouched(setDeep(touched, name, value)); + await validateForm(values); + }, + setError: (name, message) => { + setErrors(setDeep(errors, name, message)); + setTouched(setDeep(touched, name, true)); + }, + reset: (nextValues) => { + setValues(nextValues); + setErrors({}); + setTouched({}); + setSubmitCount(0); + }, + submit: () => submitForm(values), + values, + errors, + touched, + isValidating: validationState.loading, + isSubmitting: submitState.loading, + submitError: submitState.error, + isInvalid: Object.keys(errors).length > 0, + isSubmitted: submitCount > 0, + }; +} + +type DeepMap = { + [K in keyof T]?: T[K] extends any[] + ? T[K][number] extends object + ? Array> + : TValue + : T[K] extends object + ? DeepMap + : TValue; +}; + +function mapDeep(values: T, value: V): DeepMap { + return cloneDeepWith(values, (v) => { + if (typeof v !== 'object' && v !== null) { + return value; + } + }); +} + +function setDeep(values: T, name: string, value: V): T { + if (get(values, name) !== value) { + return set(cloneDeep(values), name, value); + } + return values; +} diff --git a/x-pack/plugins/security/public/components/use_html_id.ts b/x-pack/plugins/security/public/components/use_html_id.ts new file mode 100644 index 0000000000000..23666e83cbf23 --- /dev/null +++ b/x-pack/plugins/security/public/components/use_html_id.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useMemo } from 'react'; +import { htmlIdGenerator } from '@elastic/eui'; + +/** + * Generates an ID that can be used for HTML elements. + * + * @param prefix Prefix of the id to be generated + * @param suffix Suffix of the id to be generated + * + * @example + * ```typescript + * const titleId = useHtmlId('changePasswordForm', 'title'); + * + * + *

Change password

+ *
+ * ``` + */ +export function useHtmlId(prefix?: string, suffix?: string) { + return useMemo(() => htmlIdGenerator(prefix)(suffix), [prefix, suffix]); +} diff --git a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.test.tsx b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.test.tsx index c5582d3526242..b7808ffb30e74 100644 --- a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.test.tsx +++ b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.test.tsx @@ -5,141 +5,153 @@ */ import React from 'react'; -import { mountWithIntl } from '@kbn/test/jest'; - +import { shallowWithIntl } from '@kbn/test/jest'; import { RoleComboBox } from '.'; -import { EuiComboBox } from '@elastic/eui'; -import { findTestSubject } from '@kbn/test/jest'; describe('RoleComboBox', () => { - it('renders the provided list of roles via EuiComboBox options', () => { - const availableRoles = [ - { - name: 'role-1', - elasticsearch: { cluster: [], indices: [], run_as: [] }, - kibana: [], - metadata: {}, - }, - { - name: 'role-2', - elasticsearch: { cluster: [], indices: [], run_as: [] }, - kibana: [], - metadata: {}, - }, - ]; - const wrapper = mountWithIntl( - - ); - - expect(wrapper.find(EuiComboBox).props().options).toMatchInlineSnapshot(` - Array [ - Object { - "color": "default", - "data-test-subj": "roleOption-role-1", - "label": "role-1", - "value": Object { - "isDeprecated": false, + it('renders roles grouped by custom, user, admin, system and deprecated roles with correct color', () => { + const wrapper = shallowWithIntl( + { - const availableRoles = [ - { - name: 'role-1', - elasticsearch: { cluster: [], indices: [], run_as: [] }, - kibana: [], - metadata: { _deprecated: true }, - }, - ]; - const wrapper = mountWithIntl( - - ); - - expect(wrapper.find(EuiComboBox).props().options).toMatchInlineSnapshot(` - Array [ - Object { - "color": "warning", - "data-test-subj": "roleOption-role-1", - "label": "role-1", - "value": Object { - "isDeprecated": true, + { + name: 'some_admin', + elasticsearch: { cluster: [], indices: [], run_as: [] }, + kibana: [], + metadata: { _reserved: true, _deprecated: false }, }, - }, - ] - `); - }); - - it('renders the selected role names in the expanded list, coded according to deprecated status', () => { - const availableRoles = [ - { - name: 'role-1', - elasticsearch: { cluster: [], indices: [], run_as: [] }, - kibana: [], - metadata: {}, - }, - { - name: 'role-2', - elasticsearch: { cluster: [], indices: [], run_as: [] }, - kibana: [], - metadata: {}, - }, - ]; - const wrapper = mountWithIntl( -
- -
+ { + name: 'some_system', + elasticsearch: { cluster: [], indices: [], run_as: [] }, + kibana: [], + metadata: { _reserved: true, _deprecated: false }, + }, + { + name: 'deprecated_role', + elasticsearch: { cluster: [], indices: [], run_as: [] }, + kibana: [], + metadata: { _reserved: true, _deprecated: true }, + }, + ]} + selectedRoleNames={[]} + onChange={jest.fn()} + /> ); - findTestSubject(wrapper, 'comboBoxToggleListButton').simulate('click'); - - wrapper.find(EuiComboBox).setState({ isListOpen: true }); - - expect(findTestSubject(wrapper, 'rolesDropdown-renderOption')).toMatchInlineSnapshot(` - Array [ -
- -
- role-1 - -
-
-
, -
- -
- role-2 - -
-
-
, - ] + expect(wrapper).toMatchInlineSnapshot(` + `); }); }); diff --git a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.tsx b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.tsx index 5b24b296b299f..91d953c4aa29a 100644 --- a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.tsx +++ b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.tsx @@ -6,11 +6,28 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiComboBox } from '@elastic/eui'; -import { Role, isRoleDeprecated } from '../../../common/model'; -import { RoleComboBoxOption } from './role_combo_box_option'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiBadge, + EuiComboBox, + EuiComboBoxProps, + EuiComboBoxOptionOption, + EuiFlexGroup, + EuiFlexItem, +} from '@elastic/eui'; +import { + Role, + isRoleSystem, + isRoleAdmin, + isRoleReserved, + isRoleDeprecated, +} from '../../../common/model'; -interface Props { +interface Props + extends Omit< + EuiComboBoxProps, + 'onChange' | 'options' | 'selectedOptions' | 'renderOption' + > { availableRoles: Role[]; selectedRoleNames: readonly string[]; onChange: (selectedRoleNames: string[]) => void; @@ -19,43 +36,132 @@ interface Props { isDisabled?: boolean; } +type Option = EuiComboBoxOptionOption<{ + isReserved: boolean; + isDeprecated: boolean; + isSystem: boolean; + isAdmin: boolean; + deprecatedReason?: string; +}>; + export const RoleComboBox = (props: Props) => { const onRolesChange = (selectedItems: Array<{ label: string }>) => { props.onChange(selectedItems.map((item) => item.label)); }; - const roleNameToOption = (roleName: string) => { + const roleNameToOption = (roleName: string): Option => { const roleDefinition = props.availableRoles.find((role) => role.name === roleName); + const isReserved: boolean = (roleDefinition && isRoleReserved(roleDefinition)) ?? false; const isDeprecated: boolean = (roleDefinition && isRoleDeprecated(roleDefinition)) ?? false; + const isSystem: boolean = (roleDefinition && isRoleSystem(roleDefinition)) ?? false; + const isAdmin: boolean = (roleDefinition && isRoleAdmin(roleDefinition)) ?? false; return { - color: isDeprecated ? 'warning' : 'default', + color: isDeprecated ? 'warning' : isReserved ? 'primary' : undefined, 'data-test-subj': `roleOption-${roleName}`, label: roleName, value: { + isReserved, isDeprecated, + isSystem, + isAdmin, + deprecatedReason: roleDefinition?.metadata?._deprecated_reason, }, }; }; const options = props.availableRoles.map((role) => roleNameToOption(role.name)); - const selectedOptions = props.selectedRoleNames.map(roleNameToOption); + const groupedOptions = options.reduce>((acc, option) => { + const type = option.value?.isDeprecated + ? 'deprecated' + : option.value?.isSystem + ? 'system' + : option.value?.isAdmin + ? 'admin' + : option.value?.isReserved + ? 'user' + : 'custom'; + if (!acc[type]) { + acc[type] = []; + } + acc[type].push(option); + return acc; + }, {}); return ( } + renderOption={renderOption} /> ); }; + +function renderOption(option: Option) { + return ( + + {option.label} + {option.value?.isDeprecated ? ( + + + + + + ) : option.value?.isReserved ? ( + + + + + + ) : undefined} + + ); +} diff --git a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.test.tsx b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.test.tsx deleted file mode 100644 index b24a48145b461..0000000000000 --- a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.test.tsx +++ /dev/null @@ -1,57 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { shallowWithIntl } from '@kbn/test/jest'; -import { RoleComboBoxOption } from './role_combo_box_option'; - -describe('RoleComboBoxOption', () => { - it('renders a regular role correctly', () => { - const wrapper = shallowWithIntl( - - ); - - expect(wrapper).toMatchInlineSnapshot(` - - role-1 - - - `); - }); - - it('renders a deprecated role correctly', () => { - const wrapper = shallowWithIntl( - - ); - - expect(wrapper).toMatchInlineSnapshot(` - - role-1 - - (deprecated) - - `); - }); -}); diff --git a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.tsx b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.tsx deleted file mode 100644 index ae9b79c796275..0000000000000 --- a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.tsx +++ /dev/null @@ -1,31 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; - -import { i18n } from '@kbn/i18n'; - -import { EuiComboBoxOptionOption, EuiText } from '@elastic/eui'; - -interface Props { - option: EuiComboBoxOptionOption<{ isDeprecated: boolean }>; -} - -export const RoleComboBoxOption = ({ option }: Props) => { - const isDeprecated = option.value?.isDeprecated ?? false; - const deprecatedLabel = i18n.translate( - 'xpack.security.management.users.editUser.deprecatedRoleText', - { - defaultMessage: '(deprecated)', - } - ); - - return ( - - {option.label} {isDeprecated ? deprecatedLabel : ''} - - ); -}; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx index c750ec373b9f7..df5e5c8be9025 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx @@ -407,7 +407,7 @@ export const EditRolePage: FunctionComponent = ({ const onNameChange = (e: ChangeEvent) => setRole({ ...role, - name: e.target.value.replace(/\s/g, '_'), + name: e.target.value, }); const getElasticsearchPrivileges = () => { diff --git a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.test.ts b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.test.ts index 868674aec6f86..e6b9b19022f31 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.test.ts +++ b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.test.ts @@ -40,7 +40,7 @@ describe('validateRoleName', () => { expect(validator.validateRoleName(role)).toEqual({ isInvalid: true, - error: `Please provide a role name`, + error: `Please provide a role name.`, }); }); @@ -57,13 +57,30 @@ describe('validateRoleName', () => { expect(validator.validateRoleName(role)).toEqual({ isInvalid: true, - error: `Name must not exceed 1024 characters`, + error: `Name must not exceed 1024 characters.`, + }); + }); + + test('it cannot start with whitespace character', () => { + const role = { + name: ' role-name', + elasticsearch: { + cluster: [], + indices: [], + run_as: [], + }, + kibana: [], + }; + + expect(validator.validateRoleName(role)).toEqual({ + isInvalid: true, + error: `Name must not contain leading or trailing spaces.`, }); }); const charList = `!#%^&*()+=[]{}\|';:"/,<>?`.split(''); charList.forEach((element) => { - test(`it cannot support the "${element}" character`, () => { + test(`it allows the "${element}" character`, () => { const role = { name: `role-${element}`, elasticsearch: { @@ -74,10 +91,7 @@ describe('validateRoleName', () => { kibana: [], }; - expect(validator.validateRoleName(role)).toEqual({ - isInvalid: true, - error: `Name must begin with a letter or underscore and contain only letters, underscores, and numbers.`, - }); + expect(validator.validateRoleName(role)).toEqual({ isInvalid: false }); }); }); }); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts index 89b16b1467776..e0459bbd3dd0d 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts +++ b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts @@ -6,6 +6,7 @@ import { i18n } from '@kbn/i18n'; import { Role, RoleIndexPrivilege } from '../../../../common/model'; +import { NAME_REGEX, MAX_NAME_LENGTH } from '../../../../common/constants'; interface RoleValidatorOptions { shouldValidate?: boolean; @@ -41,25 +42,36 @@ export class RoleValidator { i18n.translate( 'xpack.security.management.editRole.validateRole.provideRoleNameWarningMessage', { - defaultMessage: 'Please provide a role name', + defaultMessage: 'Please provide a role name.', } ) ); } - if (role.name.length > 1024) { + if (role.name.length > MAX_NAME_LENGTH) { return invalid( i18n.translate('xpack.security.management.editRole.validateRole.nameLengthWarningMessage', { - defaultMessage: 'Name must not exceed 1024 characters', + defaultMessage: 'Name must not exceed {maxLength} characters.', + values: { maxLength: MAX_NAME_LENGTH }, }) ); } - if (!role.name.match(/^[a-zA-Z_][a-zA-Z0-9_@\-\$\.]*$/)) { + if (role.name.trim() !== role.name) { + return invalid( + i18n.translate( + 'xpack.security.management.editRole.validateRole.nameWhitespaceWarningMessage', + { + defaultMessage: `Name must not contain leading or trailing spaces.`, + } + ) + ); + } + if (!role.name.match(NAME_REGEX)) { return invalid( i18n.translate( 'xpack.security.management.editRole.validateRole.nameAllowedCharactersWarningMessage', { defaultMessage: - 'Name must begin with a letter or underscore and contain only letters, underscores, and numbers.', + 'Name must contain only letters, numbers, spaces, punctuation and printable symbols.', } ) ); diff --git a/x-pack/plugins/security/public/management/users/edit_user/change_password_flyout.tsx b/x-pack/plugins/security/public/management/users/edit_user/change_password_flyout.tsx new file mode 100644 index 0000000000000..2586b7c24bf4c --- /dev/null +++ b/x-pack/plugins/security/public/management/users/edit_user/change_password_flyout.tsx @@ -0,0 +1,286 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; +import { + EuiCallOut, + EuiFieldPassword, + EuiFlexGroup, + EuiForm, + EuiFormRow, + EuiIcon, + EuiLoadingContent, + EuiSpacer, + EuiText, + EuiFlexItem, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { useForm, ValidationErrors } from '../../../components/use_form'; +import { useCurrentUser } from '../../../components/use_current_user'; +import { FormFlyout } from '../../../components/form_flyout'; +import { UserAPIClient } from '..'; + +export interface ChangePasswordFormValues { + current_password?: string; + password: string; + confirm_password: string; +} + +export interface ChangePasswordFlyoutProps { + username: string; + defaultValues?: ChangePasswordFormValues; + onCancel(): void; + onSuccess?(): void; +} + +export const ChangePasswordFlyout: FunctionComponent = ({ + username, + defaultValues = { + current_password: '', + password: '', + confirm_password: '', + }, + onSuccess, + onCancel, +}) => { + const { services } = useKibana(); + const { value: currentUser, loading: isLoading } = useCurrentUser(); + const isCurrentUser = currentUser?.username === username; + const isSystemUser = username === 'kibana' || username === 'kibana_system'; + + const [form, eventHandlers] = useForm({ + onSubmit: async (values) => { + try { + await new UserAPIClient(services.http!).changePassword( + username, + values.password, + values.current_password + ); + services.notifications!.toasts.addSuccess( + i18n.translate('xpack.security.management.users.changePasswordFlyout.successMessage', { + defaultMessage: "Password changed for '{username}'.", + values: { username }, + }) + ); + onSuccess?.(); + } catch (error) { + if ((error as any).body?.message === 'security_exception') { + form.setError( + 'current_password', + i18n.translate( + 'xpack.security.management.users.changePasswordFlyout.currentPasswordInvalidError', + { + defaultMessage: 'Invalid password.', + } + ) + ); + } else { + services.notifications!.toasts.addDanger({ + title: i18n.translate( + 'xpack.security.management.users.changePasswordFlyout.errorMessage', + { + defaultMessage: 'Could not change password', + } + ), + text: (error as any).body?.message || error.message, + }); + throw error; + } + } + }, + validate: async (values) => { + const errors: ValidationErrors = {}; + + if (isCurrentUser) { + if (!values.current_password) { + errors.current_password = i18n.translate( + 'xpack.security.management.users.changePasswordFlyout.currentPasswordRequiredError', + { + defaultMessage: 'Enter your current password.', + } + ); + } + } + + if (!values.password) { + errors.password = i18n.translate( + 'xpack.security.management.users.changePasswordFlyout.passwordRequiredError', + { + defaultMessage: 'Enter a new password.', + } + ); + } else if (values.password.length < 6) { + errors.password = i18n.translate( + 'xpack.security.management.users.changePasswordFlyout.passwordInvalidError', + { + defaultMessage: 'Password must be at least 6 characters.', + } + ); + } else if (!values.confirm_password) { + errors.confirm_password = i18n.translate( + 'xpack.security.management.users.changePasswordFlyout.confirmPasswordRequiredError', + { + defaultMessage: 'Passwords do not match.', + } + ); + } else if (values.password !== values.confirm_password) { + errors.confirm_password = i18n.translate( + 'xpack.security.management.users.changePasswordFlyout.confirmPasswordInvalidError', + { + defaultMessage: 'Passwords do not match.', + } + ); + } + + return errors; + }, + defaultValues, + }); + + return ( + + {isLoading ? ( + + ) : ( + + {isSystemUser ? ( + <> + +

+ +

+

+ +

+
+ + + ) : undefined} + + + + + + + + + {username} + + + + + + {isCurrentUser ? ( + + + + ) : null} + + + + + + + + + {/* Hidden submit button is required for enter key to trigger form submission */} + +
+ )} +
+ ); +}; diff --git a/x-pack/plugins/security/public/management/users/edit_user/confirm_delete_users.tsx b/x-pack/plugins/security/public/management/users/edit_user/confirm_delete_users.tsx new file mode 100644 index 0000000000000..18be46ebefed0 --- /dev/null +++ b/x-pack/plugins/security/public/management/users/edit_user/confirm_delete_users.tsx @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; +import { EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import useAsyncFn from 'react-use/lib/useAsyncFn'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { ConfirmModal } from '../../../components/confirm_modal'; +import { UserAPIClient } from '..'; + +export interface ConfirmDeleteUsersProps { + usernames: string[]; + onCancel(): void; + onSuccess?(): void; +} + +export const ConfirmDeleteUsers: FunctionComponent = ({ + usernames, + onCancel, + onSuccess, +}) => { + const { services } = useKibana(); + + const [state, deleteUsers] = useAsyncFn(async () => { + for (const username of usernames) { + try { + await new UserAPIClient(services.http!).deleteUser(username); + services.notifications!.toasts.addSuccess( + i18n.translate('xpack.security.management.users.confirmDeleteUsers.successMessage', { + defaultMessage: "Deleted user '{username}'", + values: { username }, + }) + ); + onSuccess?.(); + } catch (error) { + services.notifications!.toasts.addDanger({ + title: i18n.translate('xpack.security.management.users.confirmDeleteUsers.errorMessage', { + defaultMessage: "Could not delete user '{username}'", + values: { username }, + }), + text: (error as any).body?.message || error.message, + }); + } + } + }, [services.http]); + + return ( + + +

+ +

+ {usernames.length > 1 && ( +
    + {usernames.map((username) => ( +
  • {username}
  • + ))} +
+ )} +

+ +

+
+
+ ); +}; diff --git a/x-pack/plugins/security/public/management/users/edit_user/confirm_disable_users.tsx b/x-pack/plugins/security/public/management/users/edit_user/confirm_disable_users.tsx new file mode 100644 index 0000000000000..b0a9e875c2089 --- /dev/null +++ b/x-pack/plugins/security/public/management/users/edit_user/confirm_disable_users.tsx @@ -0,0 +1,119 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; +import { EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import useAsyncFn from 'react-use/lib/useAsyncFn'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { ConfirmModal } from '../../../components/confirm_modal'; +import { UserAPIClient } from '..'; + +export interface ConfirmDisableUsersProps { + usernames: string[]; + onCancel(): void; + onSuccess?(): void; +} + +export const ConfirmDisableUsers: FunctionComponent = ({ + usernames, + onCancel, + onSuccess, +}) => { + const { services } = useKibana(); + const isSystemUser = usernames[0] === 'kibana' || usernames[0] === 'kibana_system'; + + const [state, disableUsers] = useAsyncFn(async () => { + for (const username of usernames) { + try { + await new UserAPIClient(services.http!).disableUser(username); + services.notifications!.toasts.addSuccess( + i18n.translate('xpack.security.management.users.confirmDisableUsers.successMessage', { + defaultMessage: "Deactivated user '{username}'", + values: { username }, + }) + ); + onSuccess?.(); + } catch (error) { + services.notifications!.toasts.addDanger({ + title: i18n.translate( + 'xpack.security.management.users.confirmDisableUsers.errorMessage', + { + defaultMessage: "Could not deactivate user '{username}'", + values: { username }, + } + ), + text: (error as any).body?.message || error.message, + }); + } + } + }, [services.http]); + + return ( + + {isSystemUser ? ( + +

+ +

+

+ +

+
+ ) : ( + +

+ +

+ {usernames.length > 1 && ( +
    + {usernames.map((username) => ( +
  • {username}
  • + ))} +
+ )} +
+ )} +
+ ); +}; diff --git a/x-pack/plugins/security/public/management/users/edit_user/confirm_enable_users.tsx b/x-pack/plugins/security/public/management/users/edit_user/confirm_enable_users.tsx new file mode 100644 index 0000000000000..c9589cfa17da2 --- /dev/null +++ b/x-pack/plugins/security/public/management/users/edit_user/confirm_enable_users.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; +import { EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import useAsyncFn from 'react-use/lib/useAsyncFn'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { ConfirmModal } from '../../../components/confirm_modal'; +import { UserAPIClient } from '..'; + +export interface ConfirmEnableUsersProps { + usernames: string[]; + onCancel(): void; + onSuccess?(): void; +} + +export const ConfirmEnableUsers: FunctionComponent = ({ + usernames, + onCancel, + onSuccess, +}) => { + const { services } = useKibana(); + + const [state, enableUsers] = useAsyncFn(async () => { + for (const username of usernames) { + try { + await new UserAPIClient(services.http!).enableUser(username); + services.notifications!.toasts.addSuccess( + i18n.translate('xpack.security.management.users.confirmEnableUsers.successMessage', { + defaultMessage: "Activated user '{username}'", + values: { username }, + }) + ); + onSuccess?.(); + } catch (error) { + services.notifications!.toasts.addDanger({ + title: i18n.translate('xpack.security.management.users.confirmEnableUsers.errorMessage', { + defaultMessage: "Could not activate user '{username}'", + values: { username }, + }), + text: (error as any).body?.message || error.message, + }); + } + } + }, [services.http]); + + return ( + + +

+ +

+ {usernames.length > 1 && ( +
    + {usernames.map((username) => ( +
  • {username}
  • + ))} +
+ )} +
+
+ ); +}; diff --git a/x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx b/x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx new file mode 100644 index 0000000000000..e7e3e1164ae14 --- /dev/null +++ b/x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx @@ -0,0 +1,108 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { render, fireEvent, waitFor, within } from '@testing-library/react'; +import { createMemoryHistory } from 'history'; +import { coreMock } from '../../../../../../../src/core/public/mocks'; +import { securityMock } from '../../../mocks'; +import { Providers } from '../users_management_app'; +import { CreateUserPage } from './create_user_page'; + +jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ + htmlIdGenerator: () => () => `id-${Math.random()}`, +})); + +describe('CreateUserPage', () => { + it('creates user when submitting form and redirects back', async () => { + const coreStart = coreMock.createStart(); + const history = createMemoryHistory({ initialEntries: ['/create'] }); + const authc = securityMock.createSetup().authc; + coreStart.http.post.mockResolvedValue({}); + + const { findByRole, findByLabelText } = render( + + + + ); + + fireEvent.change(await findByLabelText('Username'), { target: { value: 'jdoe' } }); + fireEvent.change(await findByLabelText('Password'), { target: { value: 'changeme' } }); + fireEvent.change(await findByLabelText('Confirm password'), { + target: { value: 'changeme' }, + }); + fireEvent.click(await findByRole('button', { name: 'Create user' })); + + await waitFor(() => { + expect(coreStart.http.post).toHaveBeenLastCalledWith('/internal/security/users/jdoe', { + body: JSON.stringify({ + password: 'changeme', + username: 'jdoe', + full_name: '', + email: '', + roles: [], + }), + }); + expect(history.location.pathname).toBe('/'); + }); + }); + + it('validates form', async () => { + const coreStart = coreMock.createStart(); + const history = createMemoryHistory({ initialEntries: ['/create'] }); + const authc = securityMock.createSetup().authc; + + coreStart.http.get.mockResolvedValueOnce([]); + coreStart.http.get.mockResolvedValueOnce([ + { + username: 'existing_username', + full_name: '', + email: '', + enabled: true, + roles: ['superuser'], + }, + ]); + + const { findAllByText, findByRole, findByLabelText } = render( + + + + ); + + fireEvent.click(await findByRole('button', { name: 'Create user' })); + + const alert = await findByRole('alert'); + within(alert).getByText(/Enter a username/i); + within(alert).getByText(/Enter a password/i); + + fireEvent.change(await findByLabelText('Username'), { target: { value: 'existing_username' } }); + + await findAllByText(/User 'existing_username' already exists/i); + + fireEvent.change(await findByLabelText('Username'), { + target: { value: ' username_with_leading_space' }, + }); + + await findAllByText(/Username must not contain leading or trailing spaces/i); + + fireEvent.change(await findByLabelText('Username'), { + target: { value: '€' }, + }); + + await findAllByText( + /Username must contain only letters, numbers, spaces, punctuation, and symbols/i + ); + + fireEvent.change(await findByLabelText('Password'), { target: { value: '111' } }); + + await findAllByText(/Password must be at least 6 characters/i); + + fireEvent.change(await findByLabelText('Password'), { target: { value: '123456' } }); + fireEvent.change(await findByLabelText('Confirm password'), { target: { value: '111' } }); + + await findAllByText(/Passwords do not match/i); + }); +}); diff --git a/x-pack/plugins/security/public/management/users/edit_user/create_user_page.tsx b/x-pack/plugins/security/public/management/users/edit_user/create_user_page.tsx new file mode 100644 index 0000000000000..6842ddb774bda --- /dev/null +++ b/x-pack/plugins/security/public/management/users/edit_user/create_user_page.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; +import { + EuiHorizontalRule, + EuiPageContent, + EuiPageContentBody, + EuiPageContentHeader, + EuiPageContentHeaderSection, + EuiTitle, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { useHistory } from 'react-router-dom'; +import { UserForm } from './user_form'; + +export const CreateUserPage: FunctionComponent = () => { + const history = useHistory(); + const backToUsers = () => history.push('/'); + + return ( + + + + +

+ +

+
+
+
+ + + + +
+ ); +}; diff --git a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.scss b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.scss deleted file mode 100644 index 727fac4782752..0000000000000 --- a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.scss +++ /dev/null @@ -1,6 +0,0 @@ -.secUsersEditPage__content { - max-width: 460px; - margin-left: auto; - margin-right: auto; - flex-grow: 0; -} diff --git a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx index 5e8c9f2d14a4c..f065c45d7080c 100644 --- a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx +++ b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx @@ -4,228 +4,440 @@ * you may not use this file except in compliance with the Elastic License. */ -import { act } from '@testing-library/react'; -import { mountWithIntl, nextTick } from '@kbn/test/jest'; -import { EditUserPage } from './edit_user_page'; import React from 'react'; -import { User, Role } from '../../../../common/model'; -import { ReactWrapper } from 'enzyme'; -import { coreMock, scopedHistoryMock } from '../../../../../../../src/core/public/mocks'; +import { + fireEvent, + render, + waitFor, + waitForElementToBeRemoved, + within, +} from '@testing-library/react'; +import { createMemoryHistory } from 'history'; +import { coreMock } from '../../../../../../../src/core/public/mocks'; import { mockAuthenticatedUser } from '../../../../common/model/authenticated_user.mock'; import { securityMock } from '../../../mocks'; -import { rolesAPIClientMock } from '../../roles/index.mock'; -import { userAPIClientMock } from '../index.mock'; -import { findTestSubject } from '@kbn/test/jest'; - -const createUser = (username: string, roles = ['idk', 'something']) => { - const user: User = { - username, - full_name: 'my full name', - email: 'foo@bar.com', - roles, - enabled: true, - }; - - if (username === 'reserved_user') { - user.metadata = { - _reserved: true, - }; - } - - if (username === 'deprecated_user') { - user.metadata = { - _reserved: true, - _deprecated: true, - _deprecated_reason: 'beacuse I said so.', - }; - } - - return user; +import { Providers } from '../users_management_app'; +import { EditUserPage } from './edit_user_page'; + +jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ + htmlIdGenerator: () => () => `id-${Math.random()}`, +})); + +const userMock = { + username: 'jdoe', + full_name: '', + email: '', + enabled: true, + roles: ['superuser'], }; -const buildClients = (user: User) => { - const apiClient = userAPIClientMock.create(); - apiClient.getUser.mockResolvedValue(user); +describe('EditUserPage', () => { + it('warns when viewing deactivated user', async () => { + const coreStart = coreMock.createStart(); + const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] }); + const authc = securityMock.createSetup().authc; + + coreStart.http.get.mockResolvedValueOnce({ + ...userMock, + enabled: false, + }); + coreStart.http.get.mockResolvedValueOnce([]); + + const { findByText } = render( + + + + ); - const rolesAPIClient = rolesAPIClientMock.create(); - rolesAPIClient.getRoles.mockImplementation(() => { - return Promise.resolve([ - { - name: 'role 1', - elasticsearch: { - cluster: ['all'], - indices: [], - run_as: [], - }, - kibana: [], - }, - { - name: 'role 2', - elasticsearch: { - cluster: [], - indices: [], - run_as: ['bar'], - }, - kibana: [], + await findByText(/User has been deactivated/i); + }); + + it('warns when viewing deprecated user', async () => { + const coreStart = coreMock.createStart(); + const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] }); + const authc = securityMock.createSetup().authc; + + coreStart.http.get.mockResolvedValueOnce({ + ...userMock, + metadata: { + _reserved: true, + _deprecated: true, + _deprecated_reason: 'Use [new_user] instead.', }, + }); + coreStart.http.get.mockResolvedValueOnce([]); + + const { findByRole, findByText } = render( + + + + ); + + await findByText(/User is deprecated/i); + await findByText(/Use .new_user. instead/i); + + fireEvent.click(await findByRole('button', { name: 'Back to users' })); + + await waitFor(() => expect(history.location.pathname).toBe('/')); + }); + + it('warns when viewing built-in user', async () => { + const coreStart = coreMock.createStart(); + const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] }); + const authc = securityMock.createSetup().authc; + + coreStart.http.get.mockResolvedValueOnce({ + ...userMock, + metadata: { _reserved: true, _deprecated: false }, + }); + coreStart.http.get.mockResolvedValueOnce([]); + + const { findByRole, findByText } = render( + + + + ); + + await findByText(/User is built in/i); + + fireEvent.click(await findByRole('button', { name: 'Back to users' })); + + await waitFor(() => expect(history.location.pathname).toBe('/')); + }); + + it('warns when selecting deprecated role', async () => { + const coreStart = coreMock.createStart(); + const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] }); + const authc = securityMock.createSetup().authc; + + coreStart.http.get.mockResolvedValueOnce({ + ...userMock, + enabled: false, + roles: ['deprecated_role'], + }); + coreStart.http.get.mockResolvedValueOnce([ { - name: 'deprecated-role', - elasticsearch: { - cluster: [], - indices: [], - run_as: ['bar'], - }, - kibana: [], + name: 'deprecated_role', metadata: { + _reserved: true, _deprecated: true, + _deprecated_reason: 'Use [new_role] instead.', }, }, - ] as Role[]); + ]); + + const { findByText } = render( + + + + ); + + await findByText(/Role .deprecated_role. is deprecated. Use .new_role. instead/i); }); - return { apiClient, rolesAPIClient }; -}; + it('updates user when submitting form and redirects back', async () => { + const coreStart = coreMock.createStart(); + const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] }); + const authc = securityMock.createSetup().authc; -function buildSecuritySetup() { - const securitySetupMock = securityMock.createSetup(); - securitySetupMock.authc.getCurrentUser.mockResolvedValue( - mockAuthenticatedUser(createUser('current_user')) - ); - return securitySetupMock; -} + coreStart.http.get.mockResolvedValueOnce(userMock); + coreStart.http.get.mockResolvedValueOnce([]); + coreStart.http.post.mockResolvedValueOnce({}); -function expectSaveButton(wrapper: ReactWrapper) { - expect(wrapper.find('EuiButton[data-test-subj="userFormSaveButton"]')).toHaveLength(1); -} + const { findByRole, findByLabelText } = render( + + + + ); -function expectMissingSaveButton(wrapper: ReactWrapper) { - expect(wrapper.find('EuiButton[data-test-subj="userFormSaveButton"]')).toHaveLength(0); -} + fireEvent.change(await findByLabelText('Full name'), { target: { value: 'John Doe' } }); + fireEvent.change(await findByLabelText('Email address'), { + target: { value: 'jdoe@elastic.co' }, + }); + fireEvent.click(await findByRole('button', { name: 'Update user' })); + + await waitFor(() => { + expect(coreStart.http.get).toHaveBeenCalledWith('/internal/security/users/jdoe'); + expect(coreStart.http.get).toHaveBeenCalledWith('/api/security/role'); + expect(coreStart.http.post).toHaveBeenLastCalledWith('/internal/security/users/jdoe', { + body: JSON.stringify({ + ...userMock, + full_name: 'John Doe', + email: 'jdoe@elastic.co', + }), + }); + expect(history.location.pathname).toBe('/'); + }); + }); -describe('EditUserPage', () => { - const history = scopedHistoryMock.create(); - - it('allows reserved users to be viewed', async () => { - const user = createUser('reserved_user'); - const { apiClient, rolesAPIClient } = buildClients(user); - const securitySetup = buildSecuritySetup(); - const wrapper = mountWithIntl( - + it('warns when user form submission fails', async () => { + const coreStart = coreMock.createStart(); + const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] }); + const authc = securityMock.createSetup().authc; + + coreStart.http.get.mockResolvedValueOnce(userMock); + coreStart.http.get.mockResolvedValueOnce([]); + coreStart.http.post.mockRejectedValueOnce(new Error('Error message')); + + const { findByRole, findByLabelText } = render( + + + ); - await waitForRender(wrapper); + fireEvent.change(await findByLabelText('Full name'), { target: { value: 'John Doe' } }); + fireEvent.change(await findByLabelText('Email address'), { + target: { value: 'jdoe@elastic.co' }, + }); + fireEvent.click(await findByRole('button', { name: 'Update user' })); + + await waitFor(() => { + expect(coreStart.http.get).toHaveBeenCalledWith('/internal/security/users/jdoe'); + expect(coreStart.http.get).toHaveBeenCalledWith('/api/security/role'); + expect(coreStart.http.post).toHaveBeenLastCalledWith('/internal/security/users/jdoe', { + body: JSON.stringify({ + ...userMock, + full_name: 'John Doe', + email: 'jdoe@elastic.co', + }), + }); + expect(coreStart.notifications.toasts.addDanger).toHaveBeenCalledWith({ + text: 'Error message', + title: "Could not update user 'jdoe'", + }); + expect(history.location.pathname).toBe('/edit/jdoe'); + }); + }); - expect(apiClient.getUser).toBeCalledTimes(1); - expect(securitySetup.authc.getCurrentUser).toBeCalledTimes(1); + it('changes password of other user when submitting form and closes dialog', async () => { + const coreStart = coreMock.createStart(); + const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] }); + const authc = securityMock.createSetup().authc; + + coreStart.http.get.mockResolvedValueOnce(userMock); + coreStart.http.get.mockResolvedValueOnce([]); + authc.getCurrentUser.mockResolvedValueOnce( + mockAuthenticatedUser({ ...userMock, username: 'elastic' }) + ); + coreStart.http.post.mockResolvedValueOnce({}); - expectMissingSaveButton(wrapper); + const { getByRole, findByRole } = render( + + + + ); + + fireEvent.click(await findByRole('button', { name: 'Change password' })); + + const dialog = getByRole('dialog'); + fireEvent.change(await within(dialog).findByLabelText('New password'), { + target: { value: 'changeme' }, + }); + fireEvent.change(within(dialog).getByLabelText('Confirm password'), { + target: { value: 'changeme' }, + }); + fireEvent.click(within(dialog).getByRole('button', { name: 'Change password' })); + + await waitForElementToBeRemoved(() => getByRole('dialog')); + expect(coreStart.http.post).toHaveBeenLastCalledWith('/internal/security/users/jdoe/password', { + body: JSON.stringify({ + newPassword: 'changeme', + }), + }); }); - it('allows new users to be created', async () => { - const user = createUser(''); - const { apiClient, rolesAPIClient } = buildClients(user); - const securitySetup = buildSecuritySetup(); - const wrapper = mountWithIntl( - + it('changes password of current user when submitting form and closes dialog', async () => { + const coreStart = coreMock.createStart(); + const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] }); + const authc = securityMock.createSetup().authc; + + coreStart.http.get.mockResolvedValueOnce(userMock); + coreStart.http.get.mockResolvedValueOnce([]); + authc.getCurrentUser.mockResolvedValueOnce(mockAuthenticatedUser(userMock)); + coreStart.http.post.mockResolvedValueOnce({}); + + const { getByRole, findByRole } = render( + + + ); - await waitForRender(wrapper); + fireEvent.click(await findByRole('button', { name: 'Change password' })); + + const dialog = await findByRole('dialog'); + fireEvent.change(await within(dialog).findByLabelText('Current password'), { + target: { value: '123456' }, + }); + fireEvent.change(await within(dialog).findByLabelText('New password'), { + target: { value: 'changeme' }, + }); + fireEvent.change(await within(dialog).findByLabelText('Confirm password'), { + target: { value: 'changeme' }, + }); + fireEvent.click(await within(dialog).findByRole('button', { name: 'Change password' })); + + await waitForElementToBeRemoved(() => getByRole('dialog')); + expect(coreStart.http.post).toHaveBeenLastCalledWith('/internal/security/users/jdoe/password', { + body: JSON.stringify({ + newPassword: 'changeme', + password: '123456', + }), + }); + }); + + it('warns when change password form submission fails', async () => { + const coreStart = coreMock.createStart(); + const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] }); + const authc = securityMock.createSetup().authc; + + coreStart.http.get.mockResolvedValueOnce(userMock); + coreStart.http.get.mockResolvedValueOnce([]); + authc.getCurrentUser.mockResolvedValueOnce( + mockAuthenticatedUser({ ...userMock, username: 'elastic' }) + ); + coreStart.http.post.mockRejectedValueOnce(new Error('Error message')); - expect(apiClient.getUser).toBeCalledTimes(0); - expect(securitySetup.authc.getCurrentUser).toBeCalledTimes(0); + const { findByRole } = render( + + + + ); - expectSaveButton(wrapper); + fireEvent.click(await findByRole('button', { name: 'Change password' })); + + const dialog = await findByRole('dialog'); + fireEvent.change(await within(dialog).findByLabelText('New password'), { + target: { value: 'changeme' }, + }); + fireEvent.change(await within(dialog).findByLabelText('Confirm password'), { + target: { value: 'changeme' }, + }); + fireEvent.click(await within(dialog).findByRole('button', { name: 'Change password' })); + + await waitFor(() => { + expect(coreStart.notifications.toasts.addDanger).toHaveBeenCalledWith({ + text: 'Error message', + title: 'Could not change password', + }); + }); }); - it('allows existing users to be edited', async () => { - const user = createUser('existing_user'); - const { apiClient, rolesAPIClient } = buildClients(user); - const securitySetup = buildSecuritySetup(); - const wrapper = mountWithIntl( - + it('validates change password form', async () => { + const coreStart = coreMock.createStart(); + const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] }); + const authc = securityMock.createSetup().authc; + + coreStart.http.get.mockResolvedValueOnce(userMock); + coreStart.http.get.mockResolvedValueOnce([]); + authc.getCurrentUser.mockResolvedValueOnce(mockAuthenticatedUser(userMock)); + coreStart.http.post.mockResolvedValueOnce({}); + + const { findByRole } = render( + + + ); - await waitForRender(wrapper); + fireEvent.click(await findByRole('button', { name: 'Change password' })); + + const dialog = await findByRole('dialog'); + fireEvent.click(await within(dialog).findByRole('button', { name: 'Change password' })); - expect(apiClient.getUser).toBeCalledTimes(1); - expect(securitySetup.authc.getCurrentUser).toBeCalledTimes(1); + await within(dialog).findByText(/Enter your current password/i); + await within(dialog).findByText(/Enter a new password/i); - expect(findTestSubject(wrapper, 'hasDeprecatedRolesAssignedHelpText')).toHaveLength(0); - expectSaveButton(wrapper); + fireEvent.change(await within(dialog).findByLabelText('Current password'), { + target: { value: 'changeme' }, + }); + + fireEvent.change(await within(dialog).findByLabelText('New password'), { + target: { value: '111' }, + }); + + await within(dialog).findAllByText(/Password must be at least 6 characters/i); + + fireEvent.change(await within(dialog).findByLabelText('New password'), { + target: { value: '123456' }, + }); + fireEvent.change(await within(dialog).findByLabelText('Confirm password'), { + target: { value: '111' }, + }); + + await within(dialog).findAllByText(/Passwords do not match/i); }); - it('warns when viewing a depreciated user', async () => { - const user = createUser('deprecated_user'); - const { apiClient, rolesAPIClient } = buildClients(user); - const securitySetup = buildSecuritySetup(); - - const wrapper = mountWithIntl( - + it('deactivates user when confirming and closes dialog', async () => { + const coreStart = coreMock.createStart(); + const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] }); + const authc = securityMock.createSetup().authc; + + coreStart.http.get.mockResolvedValueOnce(userMock); + coreStart.http.get.mockResolvedValueOnce([]); + coreStart.http.post.mockResolvedValueOnce({}); + + const { getByRole, findByRole } = render( + + + ); - await waitForRender(wrapper); - expect(apiClient.getUser).toBeCalledTimes(1); - expect(securitySetup.authc.getCurrentUser).toBeCalledTimes(1); + fireEvent.click(await findByRole('button', { name: 'Deactivate user' })); + + const dialog = getByRole('dialog'); + fireEvent.click(within(dialog).getByRole('button', { name: 'Deactivate user' })); - expect(findTestSubject(wrapper, 'deprecatedUserWarning')).toHaveLength(1); + await waitForElementToBeRemoved(() => getByRole('dialog')); + expect(coreStart.http.post).toHaveBeenLastCalledWith('/internal/security/users/jdoe/_disable'); }); - it('warns when user is assigned a deprecated role', async () => { - const user = createUser('existing_user', ['deprecated-role']); - const { apiClient, rolesAPIClient } = buildClients(user); - const securitySetup = buildSecuritySetup(); - - const wrapper = mountWithIntl( - + it('activates user when confirming and closes dialog', async () => { + const coreStart = coreMock.createStart(); + const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] }); + const authc = securityMock.createSetup().authc; + + coreStart.http.get.mockResolvedValueOnce({ ...userMock, enabled: false }); + coreStart.http.get.mockResolvedValueOnce([]); + coreStart.http.post.mockResolvedValueOnce({}); + + const { getByRole, findAllByRole } = render( + + + ); - await waitForRender(wrapper); + const [enableButton] = await findAllByRole('button', { name: 'Activate user' }); + fireEvent.click(enableButton); - expect(apiClient.getUser).toBeCalledTimes(1); - expect(securitySetup.authc.getCurrentUser).toBeCalledTimes(1); + const dialog = getByRole('dialog'); + fireEvent.click(within(dialog).getByRole('button', { name: 'Activate user' })); - expect(findTestSubject(wrapper, 'hasDeprecatedRolesAssignedHelpText')).toHaveLength(1); + await waitForElementToBeRemoved(() => getByRole('dialog')); + expect(coreStart.http.post).toHaveBeenLastCalledWith('/internal/security/users/jdoe/_enable'); }); -}); -async function waitForRender(wrapper: ReactWrapper) { - await act(async () => { - await nextTick(); - wrapper.update(); + it('deletes user when confirming and redirects back', async () => { + const coreStart = coreMock.createStart(); + const history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] }); + const authc = securityMock.createSetup().authc; + + coreStart.http.get.mockResolvedValueOnce(userMock); + coreStart.http.get.mockResolvedValueOnce([]); + coreStart.http.delete.mockResolvedValueOnce({}); + + const { getByRole, findByRole } = render( + + + + ); + + fireEvent.click(await findByRole('button', { name: 'Delete user' })); + + const dialog = getByRole('dialog'); + fireEvent.click(within(dialog).getByRole('button', { name: 'Delete user' })); + + expect(coreStart.http.delete).toHaveBeenLastCalledWith('/internal/security/users/jdoe'); + await waitFor(() => { + expect(history.location.pathname).toBe('/'); + }); }); -} +}); diff --git a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx index dc0c3336cb85f..68c01bf509b0d 100644 --- a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx +++ b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx @@ -4,615 +4,315 @@ * you may not use this file except in compliance with the Elastic License. */ -import './edit_user_page.scss'; - -import { get } from 'lodash'; -import React, { Component, Fragment, ChangeEvent } from 'react'; +import React, { FunctionComponent, useState, useEffect } from 'react'; import { + EuiAvatar, EuiButton, EuiCallOut, - EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, - EuiLink, - EuiTitle, - EuiForm, - EuiFormRow, - EuiIcon, - EuiText, - EuiFieldText, + EuiHorizontalRule, EuiPageContent, + EuiPageContentBody, EuiPageContentHeader, EuiPageContentHeaderSection, - EuiPageContentBody, - EuiHorizontalRule, + EuiPanel, EuiSpacer, + EuiText, + EuiTitle, + EuiDescriptionList, + EuiDescriptionListTitle, + EuiDescriptionListDescription, } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { PublicMethodsOf } from '@kbn/utility-types'; -import { NotificationsStart, ScopedHistory } from 'src/core/public'; -import { User, EditUser, Role, isRoleDeprecated } from '../../../../common/model'; -import { AuthenticationServiceSetup } from '../../../authentication'; -import { RolesAPIClient } from '../../roles'; -import { ConfirmDeleteUsers, ChangePasswordForm } from '../components'; -import { UserValidator, UserValidationResult } from './validate_user'; -import { RoleComboBox } from '../../role_combo_box'; -import { isUserDeprecated, getExtendedUserDeprecationNotice, isUserReserved } from '../user_utils'; +import { useHistory } from 'react-router-dom'; +import useAsyncFn from 'react-use/lib/useAsyncFn'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { getUserDisplayName } from '../../../../common/model'; +import { isUserDeprecated, isUserReserved } from '../user_utils'; +import { UserForm } from './user_form'; +import { ChangePasswordFlyout } from './change_password_flyout'; +import { ConfirmDisableUsers } from './confirm_disable_users'; +import { ConfirmEnableUsers } from './confirm_enable_users'; +import { ConfirmDeleteUsers } from './confirm_delete_users'; import { UserAPIClient } from '..'; -interface Props { - username?: string; - userAPIClient: PublicMethodsOf; - rolesAPIClient: PublicMethodsOf; - authc: AuthenticationServiceSetup; - notifications: NotificationsStart; - history: ScopedHistory; -} - -interface State { - isLoaded: boolean; - isNewUser: boolean; - currentUser: User | null; - showChangePasswordForm: boolean; - showDeleteConfirmation: boolean; - user: EditUser; - roles: Role[]; - selectedRoles: readonly string[]; - formError: UserValidationResult | null; +export interface EditUserPageProps { + username: string; } -export class EditUserPage extends Component { - private validator: UserValidator; - - constructor(props: Props) { - super(props); - this.validator = new UserValidator({ shouldValidate: false }); - this.state = { - isLoaded: false, - isNewUser: true, - currentUser: null, - showChangePasswordForm: false, - showDeleteConfirmation: false, - user: { - email: '', - username: '', - full_name: '', - roles: [], - enabled: true, - password: '', - confirmPassword: '', - }, - roles: [], - selectedRoles: [], - formError: null, - }; - } - - public async componentDidMount() { - await this.setCurrentUser(); - } - - public async componentDidUpdate(prevProps: Props) { - if (prevProps.username !== this.props.username) { - await this.setCurrentUser(); +export type EditUserPageAction = + | 'changePassword' + | 'disableUser' + | 'enableUser' + | 'deleteUser' + | 'none'; + +export const EditUserPage: FunctionComponent = ({ username }) => { + const { services } = useKibana(); + const history = useHistory(); + const [{ value: user, error }, getUser] = useAsyncFn( + () => new UserAPIClient(services.http!).getUser(username), + [services.http] + ); + const [action, setAction] = useState('none'); + + const backToUsers = () => history.push('/'); + + useEffect(() => { + getUser(); + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + useEffect(() => { + if (error) { + backToUsers(); } - } + }, [error]); // eslint-disable-line react-hooks/exhaustive-deps - private backToUserList() { - this.props.history.push('/'); + if (!user) { + return null; } - private async setCurrentUser() { - const { username, userAPIClient, rolesAPIClient, notifications, authc } = this.props; - let { user, currentUser } = this.state; - if (username) { - try { - user = { - ...(await userAPIClient.getUser(username)), - password: '', - confirmPassword: '', - }; - currentUser = await authc.getCurrentUser(); - } catch (err) { - notifications.toasts.addDanger({ - title: i18n.translate('xpack.security.management.users.editUser.errorLoadingUserTitle', { - defaultMessage: 'Error loading user', - }), - text: get(err, 'body.message') || err.message, - }); - return this.backToUserList(); - } - } - - let roles: Role[] = []; - try { - roles = await rolesAPIClient.getRoles(); - } catch (err) { - notifications.toasts.addDanger({ - title: i18n.translate('xpack.security.management.users.editUser.errorLoadingRolesTitle', { - defaultMessage: 'Error loading roles', - }), - text: get(err, 'body.message') || err.message, - }); - } - - this.setState({ - isLoaded: true, - isNewUser: !username, - currentUser, - user, - roles, - selectedRoles: user.roles || [], - }); - } - - private handleDelete = (usernames: string[], errors: string[]) => { - if (errors.length === 0) { - this.backToUserList(); - } - }; - - private saveUser = async () => { - this.validator.enableValidation(); - - const result = this.validator.validateForSave(this.state.user, this.state.isNewUser); - if (result.isInvalid) { - this.setState({ - formError: result, - }); - } else { - this.setState({ - formError: null, - }); - const { userAPIClient } = this.props; - const { user, isNewUser, selectedRoles } = this.state; - const userToSave: EditUser = { ...user }; - if (!isNewUser) { - delete userToSave.password; - } - delete userToSave.confirmPassword; - userToSave.roles = [...selectedRoles]; - try { - await userAPIClient.saveUser(userToSave); - this.props.notifications.toasts.addSuccess( - i18n.translate( - 'xpack.security.management.users.editUser.userSuccessfullySavedNotificationMessage', - { - defaultMessage: 'Saved user {message}', - values: { message: user.username }, - } - ) - ); - - this.backToUserList(); - } catch (e) { - this.props.notifications.toasts.addDanger( - i18n.translate('xpack.security.management.users.editUser.savingUserErrorMessage', { - defaultMessage: 'Error saving user: {message}', - values: { message: get(e, 'body.message', 'Unknown error') }, - }) - ); - } - } - }; - - private passwordFields = () => { - return ( - - - - - - - - - ); - }; - - private changePasswordForm = () => { - const { showChangePasswordForm, user, currentUser } = this.state; - - const userIsLoggedInUser = Boolean( - currentUser && user.username && user.username === currentUser.username - ); - - if (!showChangePasswordForm) { - return null; - } - return ( - + const isReservedUser = isUserReserved(user); + const isDeprecatedUser = isUserDeprecated(user); + const displayName = getUserDisplayName(user); + + return ( + + + + + + + + + +

{displayName}

+
+ {user.email} +
+
+
+
+ - {user.username === 'kibana' || user.username === 'kibana_system' ? ( - + {isDeprecatedUser ? ( + <> + } + iconType="alert" color="warning" - iconType="help" > -

+ {user.metadata?._deprecated_reason?.replace(/\[(.+)\]/, "'$1'")} + + + + ) : isReservedUser ? ( + <> + + } + iconType="lock" + /> + + + ) : user.enabled === false ? ( + <> + -

+ } + > + setAction('enableUser')} size="s"> + +
-
- ) : null} - + ) : undefined} + + -
- ); - }; - - private toggleChangePasswordForm = () => { - const { showChangePasswordForm } = this.state; - this.setState({ showChangePasswordForm: !showChangePasswordForm }); - }; - - private onUsernameChange = (e: ChangeEvent) => { - const user = { - ...this.state.user, - username: e.target.value || '', - }; - const formError = this.validator.validateForSave(user, this.state.isNewUser); - - this.setState({ - user, - formError, - }); - }; - private onEmailChange = (e: ChangeEvent) => { - const user = { - ...this.state.user, - email: e.target.value || '', - }; - const formError = this.validator.validateForSave(user, this.state.isNewUser); - - this.setState({ - user, - formError, - }); - }; - - private onFullNameChange = (e: ChangeEvent) => { - const user = { - ...this.state.user, - full_name: e.target.value || '', - }; - const formError = this.validator.validateForSave(user, this.state.isNewUser); - - this.setState({ - user, - formError, - }); - }; - - private onPasswordChange = (e: ChangeEvent) => { - const user = { - ...this.state.user, - password: e.target.value || '', - }; - const formError = this.validator.validateForSave(user, this.state.isNewUser); - - this.setState({ - user, - formError, - }); - }; - - private onConfirmPasswordChange = (e: ChangeEvent) => { - const user = { - ...this.state.user, - confirmPassword: e.target.value || '', - }; - const formError = this.validator.validateForSave(user, this.state.isNewUser); - - this.setState({ - user, - formError, - }); - }; - - private onRolesChange = (selectedRoles: string[]) => { - this.setState({ - selectedRoles, - }); - }; - - private cannotSaveUser = () => { - const { user, isNewUser } = this.state; - const result = this.validator.validateForSave(user, isNewUser); - return result.isInvalid; - }; - - private onCancelDelete = () => { - this.setState({ showDeleteConfirmation: false }); - }; - - public render() { - const { - user, - selectedRoles, - roles, - showChangePasswordForm, - isNewUser, - showDeleteConfirmation, - } = this.state; - const reserved = isUserReserved(user); - if (!user || !roles) { - return null; - } - - if (!this.state.isLoaded) { - return null; - } - - const hasAnyDeprecatedRolesAssigned = selectedRoles.some((selected) => { - const role = roles.find((r) => r.name === selected); - return role && isRoleDeprecated(role); - }); + {action === 'changePassword' ? ( + setAction('none')} + onSuccess={() => setAction('none')} + /> + ) : action === 'disableUser' ? ( + setAction('none')} + onSuccess={() => { + setAction('none'); + getUser(); + }} + /> + ) : action === 'enableUser' ? ( + setAction('none')} + onSuccess={() => { + setAction('none'); + getUser(); + }} + /> + ) : action === 'deleteUser' ? ( + setAction('none')} + onSuccess={backToUsers} + /> + ) : undefined} - const roleHelpText = hasAnyDeprecatedRolesAssigned ? ( - - - - ) : undefined; + + - return ( -
- - - - -

- {isNewUser ? ( + + + + + + + + + + + + + + setAction('changePassword')} size="s"> + + + + + + + + {user.enabled === false ? ( + + + + + - ) : ( + + - )} -

-
-
- {reserved && ( - - - - )} -
- - {reserved && ( - - -

+ + + + + setAction('enableUser')} size="s"> + + + + + + ) : ( + + + + + -

-
- -
- )} - - {isUserDeprecated(user) && ( - - - - - )} - - {showDeleteConfirmation ? ( - - ) : null} - - - - - - {isNewUser ? this.passwordFields() : null} - {reserved ? null : ( - - - - - - - - - )} - - - - - {isNewUser || showChangePasswordForm ? null : ( - - + + - - - )} - {this.changePasswordForm()} - - - - {reserved && ( - this.backToUserList()}> + + + + + setAction('disableUser')} size="s"> - )} - {reserved ? null : ( - - - this.saveUser()} - > - {isNewUser ? ( - - ) : ( - - )} - - - - this.backToUserList()} - > + + + + )} + + {!isReservedUser && ( + <> + + + + + + - - - - {isNewUser || reserved ? null : ( - - { - this.setState({ showDeleteConfirmation: true }); - }} - data-test-subj="userFormDeleteButton" - color="danger" - > - - - - )} - - )} - -
-
-
- ); - } -} + + + + + + + + setAction('deleteUser')} size="s" color="danger"> + + + + + + + )} + + + ); +}; diff --git a/x-pack/plugins/security/public/management/users/edit_user/index.ts b/x-pack/plugins/security/public/management/users/edit_user/index.ts index 92eb17b9ebd36..30069d8e97c31 100644 --- a/x-pack/plugins/security/public/management/users/edit_user/index.ts +++ b/x-pack/plugins/security/public/management/users/edit_user/index.ts @@ -5,3 +5,4 @@ */ export { EditUserPage } from './edit_user_page'; +export { CreateUserPage } from './create_user_page'; diff --git a/x-pack/plugins/security/public/management/users/edit_user/user_form.tsx b/x-pack/plugins/security/public/management/users/edit_user/user_form.tsx new file mode 100644 index 0000000000000..daa488d674fbb --- /dev/null +++ b/x-pack/plugins/security/public/management/users/edit_user/user_form.tsx @@ -0,0 +1,466 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent, useEffect, useCallback } from 'react'; +import { + EuiButton, + EuiButtonEmpty, + EuiDescribedFormGroup, + EuiFieldPassword, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiFormRow, + EuiSpacer, + EuiTextColor, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import useAsyncFn from 'react-use/lib/useAsyncFn'; +import { throttle } from 'lodash'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { User, Role, isRoleDeprecated } from '../../../../common/model'; +import { NAME_REGEX, MAX_NAME_LENGTH } from '../../../../common/constants'; +import { useForm, ValidationErrors } from '../../../components/use_form'; +import { DocLink } from '../../../components/doc_link'; +import { RolesAPIClient } from '../../roles'; +import { RoleComboBox } from '../../role_combo_box'; +import { UserAPIClient } from '..'; + +export const THROTTLE_USERS_WAIT = 10000; + +export interface UserFormValues { + username?: string; + full_name: string; + email: string; + password?: string; + confirm_password?: string; + roles: readonly string[]; +} + +export interface UserFormProps { + isNewUser?: boolean; + isReservedUser?: boolean; + isCurrentUser?: boolean; + defaultValues?: UserFormValues; + onCancel(): void; + onSuccess?(): void; +} + +const defaultDefaultValues: UserFormValues = { + username: '', + password: '', + confirm_password: '', + full_name: '', + email: '', + roles: [], +}; + +export const UserForm: FunctionComponent = ({ + isNewUser = false, + isReservedUser = false, + defaultValues = defaultDefaultValues, + onSuccess, + onCancel, +}) => { + const { services } = useKibana(); + + const [rolesState, getRoles] = useAsyncFn(() => new RolesAPIClient(services.http!).getRoles(), [ + services.http, + ]); + + // eslint-disable-next-line react-hooks/exhaustive-deps + const getUsersThrottled = useCallback( + throttle(() => new UserAPIClient(services.http!).getUsers(), THROTTLE_USERS_WAIT), + [services.http] + ); + + const [form, eventHandlers] = useForm({ + onSubmit: async (values) => { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { password, confirm_password, ...rest } = values; + const user = isNewUser ? { password, ...rest } : rest; + try { + await new UserAPIClient(services.http!).saveUser(user as User); + services.notifications!.toasts.addSuccess( + isNewUser + ? i18n.translate('xpack.security.management.users.userForm.createSuccessMessage', { + defaultMessage: "Created user '{username}'", + values: { username: user.username }, + }) + : i18n.translate('xpack.security.management.users.userForm.updateSuccessMessage', { + defaultMessage: "Updated user '{username}'", + values: { username: user.username }, + }) + ); + onSuccess?.(); + } catch (error) { + services.notifications!.toasts.addDanger({ + title: isNewUser + ? i18n.translate('xpack.security.management.users.userForm.createErrorMessage', { + defaultMessage: "Could not create user '{username}'", + values: { username: user.username }, + }) + : i18n.translate('xpack.security.management.users.userForm.updateErrorMessage', { + defaultMessage: "Could not update user '{username}'", + values: { username: user.username }, + }), + text: (error as any).body?.message || error.message, + }); + throw error; + } + }, + validate: async (values) => { + const errors: ValidationErrors = {}; + + if (isNewUser) { + if (!values.username) { + errors.username = i18n.translate( + 'xpack.security.management.users.userForm.usernameRequiredError', + { + defaultMessage: 'Enter a username.', + } + ); + } else if (values.username.length > MAX_NAME_LENGTH) { + errors.username = i18n.translate( + 'xpack.security.management.users.userForm.usernameMaxLengthError', + { + defaultMessage: 'Username must not exceed {maxLength} characters.', + values: { maxLength: MAX_NAME_LENGTH }, + } + ); + } else if (values.username.trim() !== values.username) { + errors.username = i18n.translate( + 'xpack.security.management.users.userForm.usernameWhitespaceError', + { + defaultMessage: `Username must not contain leading or trailing spaces.`, + } + ); + } else if (!values.username.match(NAME_REGEX)) { + errors.username = i18n.translate( + 'xpack.security.management.users.userForm.usernameInvalidError', + { + defaultMessage: + 'Username must contain only letters, numbers, spaces, punctuation, and symbols.', + } + ); + } else { + try { + const users = await getUsersThrottled(); + if (users.some((user) => user.username === values.username)) { + errors.username = i18n.translate( + 'xpack.security.management.users.userForm.usernameTakenError', + { + defaultMessage: "User '{username}' already exists.", + values: { username: values.username }, + } + ); + } + } catch (error) {} // eslint-disable-line no-empty + } + + if (!values.password) { + errors.password = i18n.translate( + 'xpack.security.management.users.userForm.passwordRequiredError', + { + defaultMessage: 'Enter a password.', + } + ); + } else if (values.password.length < 6) { + errors.password = i18n.translate( + 'xpack.security.management.users.userForm.passwordInvalidError', + { + defaultMessage: 'Password must be at least 6 characters.', + } + ); + } else if (!values.confirm_password) { + errors.confirm_password = i18n.translate( + 'xpack.security.management.users.userForm.confirmPasswordRequiredError', + { + defaultMessage: 'Passwords do not match.', + } + ); + } else if (values.password !== values.confirm_password) { + errors.confirm_password = i18n.translate( + 'xpack.security.management.users.userForm.confirmPasswordInvalidError', + { + defaultMessage: 'Passwords do not match.', + } + ); + } + } + + return errors; + }, + defaultValues, + }); + + useEffect(() => { + form.reset(defaultValues); + }, [defaultValues]); // eslint-disable-line react-hooks/exhaustive-deps + + useEffect(() => { + getRoles(); + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + const availableRoles = rolesState.value ?? []; + const selectedRoleNames = form.values.roles ?? []; + const deprecatedRoles = selectedRoleNames.reduce((roles, name) => { + const role = availableRoles.find((r) => r.name === name); + if (role && isRoleDeprecated(role)) { + roles.push(role); + } + return roles; + }, []); + + return ( + + + + + } + description={i18n.translate('xpack.security.management.users.userForm.profileDescription', { + defaultMessage: 'Provide personal details.', + })} + > + + + + + {!isReservedUser ? ( + <> + + + + + + + + ) : undefined} + + + {isNewUser ? ( + + + + } + description={i18n.translate( + 'xpack.security.management.users.userForm.passwordDescription', + { + defaultMessage: 'Protect your data with a strong password.', + } + )} + > + + + + + + + + ) : null} + + + + + } + description={i18n.translate( + 'xpack.security.management.users.userForm.privilegesDescription', + { + defaultMessage: 'Assign roles to manage access and permissions.', + } + )} + > + 0 ? ( + + {deprecatedRoles.map((role) => ( +

+ +

+ ))} +
+ ) : ( + + + + ) + } + > + form.setValue('roles', value)} + isLoading={rolesState.loading} + isDisabled={isReservedUser} + /> +
+ + + {isReservedUser ? ( + + + + + + + + ) : ( + + + + {isNewUser ? ( + + ) : ( + + )} + + + + + + + + + )} +
+
+ ); +}; diff --git a/x-pack/plugins/security/public/management/users/edit_user/validate_user.test.ts b/x-pack/plugins/security/public/management/users/edit_user/validate_user.test.ts deleted file mode 100644 index 6050e1868a759..0000000000000 --- a/x-pack/plugins/security/public/management/users/edit_user/validate_user.test.ts +++ /dev/null @@ -1,128 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { UserValidator, UserValidationResult } from './validate_user'; -import { User, EditUser } from '../../../../common/model'; - -function expectValid(result: UserValidationResult) { - expect(result.isInvalid).toBe(false); -} - -function expectInvalid(result: UserValidationResult) { - expect(result.isInvalid).toBe(true); -} - -describe('UserValidator', () => { - describe('#validateUsername', () => { - it(`returns 'valid' if validation is disabled`, () => { - expectValid(new UserValidator().validateUsername({} as User)); - }); - - it(`returns 'invalid' if username is missing`, () => { - expectInvalid(new UserValidator({ shouldValidate: true }).validateUsername({} as User)); - }); - - it(`returns 'invalid' if username contains invalid characters`, () => { - expectInvalid( - new UserValidator({ shouldValidate: true }).validateUsername({ - username: '!@#$%^&*()', - } as User) - ); - }); - - it(`returns 'valid' for correct usernames`, () => { - expectValid( - new UserValidator({ shouldValidate: true }).validateUsername({ - username: 'my_user', - } as User) - ); - }); - }); - - describe('#validateEmail', () => { - it(`returns 'valid' if validation is disabled`, () => { - expectValid(new UserValidator().validateEmail({} as EditUser)); - }); - - it(`returns 'valid' if email is missing`, () => { - expectValid(new UserValidator({ shouldValidate: true }).validateEmail({} as EditUser)); - }); - - it(`returns 'invalid' for invalid emails`, () => { - expectInvalid( - new UserValidator({ shouldValidate: true }).validateEmail({ - email: 'asf', - } as EditUser) - ); - }); - - it(`returns 'valid' for correct emails`, () => { - expectValid( - new UserValidator({ shouldValidate: true }).validateEmail({ - email: 'foo@bar.co', - } as EditUser) - ); - }); - }); - - describe('#validatePassword', () => { - it(`returns 'valid' if validation is disabled`, () => { - expectValid(new UserValidator().validatePassword({} as EditUser)); - }); - - it(`returns 'invalid' if password is missing`, () => { - expectInvalid(new UserValidator({ shouldValidate: true }).validatePassword({} as EditUser)); - }); - - it(`returns 'invalid' for invalid password`, () => { - expectInvalid( - new UserValidator({ shouldValidate: true }).validatePassword({ - password: 'short', - } as EditUser) - ); - }); - - it(`returns 'valid' for correct passwords`, () => { - expectValid( - new UserValidator({ shouldValidate: true }).validatePassword({ - password: 'changeme', - } as EditUser) - ); - }); - }); - - describe('#validateConfirmPassword', () => { - it(`returns 'valid' if validation is disabled`, () => { - expectValid(new UserValidator().validateConfirmPassword({} as EditUser)); - }); - - it(`returns 'invalid' if confirm password is missing`, () => { - expectInvalid( - new UserValidator({ shouldValidate: true }).validateConfirmPassword({ - password: 'changeme', - } as EditUser) - ); - }); - - it(`returns 'invalid' for mismatched passwords`, () => { - expectInvalid( - new UserValidator({ shouldValidate: true }).validateConfirmPassword({ - password: 'changeme', - confirmPassword: 'changeyou', - } as EditUser) - ); - }); - - it(`returns 'valid' for correct passwords`, () => { - expectValid( - new UserValidator({ shouldValidate: true }).validateConfirmPassword({ - password: 'changeme', - confirmPassword: 'changeme', - } as EditUser) - ); - }); - }); -}); diff --git a/x-pack/plugins/security/public/management/users/edit_user/validate_user.ts b/x-pack/plugins/security/public/management/users/edit_user/validate_user.ts deleted file mode 100644 index 5edd96c68bf0d..0000000000000 --- a/x-pack/plugins/security/public/management/users/edit_user/validate_user.ts +++ /dev/null @@ -1,142 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; -import { User, EditUser } from '../../../../common/model'; - -interface UserValidatorOptions { - shouldValidate?: boolean; -} - -export interface UserValidationResult { - isInvalid: boolean; - error?: string; -} - -const validEmailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; -const validUsernameRegex = /[a-zA-Z_][a-zA-Z0-9_@\-\$\.]*/; - -export class UserValidator { - private shouldValidate?: boolean; - - constructor(options: UserValidatorOptions = {}) { - this.shouldValidate = options.shouldValidate; - } - - public enableValidation() { - this.shouldValidate = true; - } - - public disableValidation() { - this.shouldValidate = false; - } - - public validateUsername(user: User): UserValidationResult { - if (!this.shouldValidate) { - return valid(); - } - - const { username } = user; - if (!username) { - return invalid( - i18n.translate('xpack.security.management.users.editUser.requiredUsernameErrorMessage', { - defaultMessage: 'Username is required', - }) - ); - } else if (username && !username.match(validUsernameRegex)) { - return invalid( - i18n.translate( - 'xpack.security.management.users.editUser.usernameAllowedCharactersErrorMessage', - { - defaultMessage: - 'Username must begin with a letter or underscore and contain only letters, underscores, and numbers', - } - ) - ); - } - - return valid(); - } - - public validateEmail(user: EditUser): UserValidationResult { - if (!this.shouldValidate) { - return valid(); - } - - const { email } = user; - if (email && !email.match(validEmailRegex)) { - return invalid( - i18n.translate('xpack.security.management.users.editUser.validEmailRequiredErrorMessage', { - defaultMessage: 'Email address is invalid', - }) - ); - } - return valid(); - } - - public validatePassword(user: EditUser): UserValidationResult { - if (!this.shouldValidate) { - return valid(); - } - - const { password } = user; - if (!password || password.length < 6) { - return invalid( - i18n.translate('xpack.security.management.users.editUser.passwordLengthErrorMessage', { - defaultMessage: 'Password must be at least 6 characters', - }) - ); - } - return valid(); - } - - public validateConfirmPassword(user: EditUser): UserValidationResult { - if (!this.shouldValidate) { - return valid(); - } - - const { password, confirmPassword } = user; - if (password && confirmPassword !== null && password !== confirmPassword) { - return invalid( - i18n.translate('xpack.security.management.users.editUser.passwordDoNotMatchErrorMessage', { - defaultMessage: 'Passwords do not match', - }) - ); - } - return valid(); - } - - public validateForSave(user: EditUser, isNewUser: boolean): UserValidationResult { - const { isInvalid: isUsernameInvalid } = this.validateUsername(user); - const { isInvalid: isEmailInvalid } = this.validateEmail(user); - let isPasswordInvalid = false; - let isConfirmPasswordInvalid = false; - - if (isNewUser) { - isPasswordInvalid = this.validatePassword(user).isInvalid; - isConfirmPasswordInvalid = this.validateConfirmPassword(user).isInvalid; - } - - if (isUsernameInvalid || isEmailInvalid || isPasswordInvalid || isConfirmPasswordInvalid) { - return invalid(); - } - - return valid(); - } -} - -function invalid(error?: string): UserValidationResult { - return { - isInvalid: true, - error, - }; -} - -function valid(): UserValidationResult { - return { - isInvalid: false, - }; -} diff --git a/x-pack/plugins/security/public/management/users/user_api_client.mock.ts b/x-pack/plugins/security/public/management/users/user_api_client.mock.ts index 7223f78d57fdc..54c7ae8f4ae3b 100644 --- a/x-pack/plugins/security/public/management/users/user_api_client.mock.ts +++ b/x-pack/plugins/security/public/management/users/user_api_client.mock.ts @@ -9,6 +9,8 @@ export const userAPIClientMock = { getUsers: jest.fn(), getUser: jest.fn(), deleteUser: jest.fn(), + enableUser: jest.fn(), + disableUser: jest.fn(), saveUser: jest.fn(), changePassword: jest.fn(), }), diff --git a/x-pack/plugins/security/public/management/users/user_api_client.ts b/x-pack/plugins/security/public/management/users/user_api_client.ts index 61dd09d2c5e3d..b96596ba7c653 100644 --- a/x-pack/plugins/security/public/management/users/user_api_client.ts +++ b/x-pack/plugins/security/public/management/users/user_api_client.ts @@ -30,7 +30,7 @@ export class UserAPIClient { }); } - public async changePassword(username: string, password: string, currentPassword: string) { + public async changePassword(username: string, password: string, currentPassword?: string) { const data: Record = { newPassword: password, }; @@ -42,4 +42,12 @@ export class UserAPIClient { body: JSON.stringify(data), }); } + + public async disableUser(username: string) { + await this.http.post(`${usersUrl}/${encodeURIComponent(username)}/_disable`); + } + + public async enableUser(username: string) { + await this.http.post(`${usersUrl}/${encodeURIComponent(username)}/_enable`); + } } diff --git a/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.tsx b/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.tsx index 37747f9a1ccfa..3b1705d2bc46b 100644 --- a/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.tsx +++ b/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.tsx @@ -237,7 +237,7 @@ export class UsersGridPage extends Component { ({ - UsersGridPage: (props: any) => `Users Page: ${JSON.stringify(props)}`, -})); - -jest.mock('./edit_user', () => ({ - EditUserPage: (props: any) => `User Edit Page: ${JSON.stringify(props)}`, -})); - -import { usersManagementApp } from './users_management_app'; - import { coreMock, scopedHistoryMock } from '../../../../../../src/core/public/mocks'; import { securityMock } from '../../mocks'; +import { usersManagementApp } from './users_management_app'; -async function mountApp(basePath: string, pathname: string) { - const container = document.createElement('div'); - const setBreadcrumbs = jest.fn(); +const element = document.body.appendChild(document.createElement('div')); - const unmount = await usersManagementApp - .create({ - authc: securityMock.createSetup().authc, - getStartServices: coreMock.createSetup().getStartServices as any, - }) - .mount({ - basePath, - element: container, +describe('usersManagementApp', () => { + it('renders application and sets breadcrumbs', async () => { + const { getStartServices } = coreMock.createSetup(); + const coreStartMock = coreMock.createStart(); + getStartServices.mockResolvedValue([coreStartMock, {}, {}]); + const { authc } = securityMock.createSetup(); + + const setBreadcrumbs = jest.fn(); + const history = scopedHistoryMock.create({ pathname: '/create' }); + + const unmount = await usersManagementApp.create({ authc, getStartServices }).mount({ + basePath: '/', + element, setBreadcrumbs, - history: scopedHistoryMock.create({ pathname }), + history, }); - return { unmount, container, setBreadcrumbs }; -} - -describe('usersManagementApp', () => { - it('create() returns proper management app descriptor', () => { - expect( - usersManagementApp.create({ - authc: securityMock.createSetup().authc, - getStartServices: coreMock.createSetup().getStartServices as any, - }) - ).toMatchInlineSnapshot(` - Object { - "id": "users", - "mount": [Function], - "order": 10, - "title": "Users", - } - `); - }); - - it('mount() works for the `grid` page', async () => { - const { setBreadcrumbs, container, unmount } = await mountApp('/', '/'); - - expect(setBreadcrumbs).toHaveBeenCalledTimes(1); - expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Users' }]); - expect(container).toMatchInlineSnapshot(` -
- Users Page: {"notifications":{"toasts":{}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}}} -
- `); - - unmount(); - - expect(container).toMatchInlineSnapshot(`
`); - }); - - it('mount() works for the `create user` page', async () => { - const { setBreadcrumbs, container, unmount } = await mountApp('/', '/edit'); - - expect(setBreadcrumbs).toHaveBeenCalledTimes(1); - expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Users' }, { text: 'Create' }]); - expect(container).toMatchInlineSnapshot(` -
- User Edit Page: {"authc":{},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"notifications":{"toasts":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}}} -
- `); - - unmount(); - - expect(container).toMatchInlineSnapshot(`
`); - }); - - it('mount() works for the `edit user` page', async () => { - const userName = 'foo@bar.com'; - - const { setBreadcrumbs, container, unmount } = await mountApp('/', `/edit/${userName}`); - - expect(setBreadcrumbs).toHaveBeenCalledTimes(1); - expect(setBreadcrumbs).toHaveBeenCalledWith([ - { href: `/`, text: 'Users' }, - { href: `/edit/${encodeURIComponent(userName)}`, text: userName }, + expect(setBreadcrumbs).toHaveBeenLastCalledWith([ + { href: '/', text: 'Users' }, + { href: '/create', text: 'Create' }, ]); - expect(container).toMatchInlineSnapshot(` -
- User Edit Page: {"authc":{},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"notifications":{"toasts":{}},"username":"foo@bar.com","history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/foo@bar.com","search":"","hash":""}}} -
- `); unmount(); - - expect(container).toMatchInlineSnapshot(`
`); - }); - - const usernames = ['foo@bar.com', 'foo&bar.com', 'some 安全性 user']; - usernames.forEach((username) => { - it( - 'mount() properly encodes user name in `edit user` page link in breadcrumbs for user ' + - username, - async () => { - const { setBreadcrumbs } = await mountApp('/', `/edit/${username}`); - - expect(setBreadcrumbs).toHaveBeenCalledTimes(1); - expect(setBreadcrumbs).toHaveBeenCalledWith([ - { href: `/`, text: 'Users' }, - { - href: `/edit/${encodeURIComponent(username)}`, - text: username, - }, - ]); - } - ); }); }); diff --git a/x-pack/plugins/security/public/management/users/users_management_app.tsx b/x-pack/plugins/security/public/management/users/users_management_app.tsx index 2f16f85d5fcae..cbb303d1a128d 100644 --- a/x-pack/plugins/security/public/management/users/users_management_app.tsx +++ b/x-pack/plugins/security/public/management/users/users_management_app.tsx @@ -4,14 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { FunctionComponent } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { Router, Route, Switch, useParams } from 'react-router-dom'; +import { Router, Route, Switch, Redirect, RouteComponentProps } from 'react-router-dom'; +import { History } from 'history'; import { i18n } from '@kbn/i18n'; -import { StartServicesAccessor } from 'src/core/public'; +import { I18nProvider } from '@kbn/i18n/react'; +import { StartServicesAccessor, CoreStart } from '../../../../../../src/core/public'; import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public'; +import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; import { AuthenticationServiceSetup } from '../../authentication'; import { PluginStartDependencies } from '../../plugin'; +import { + BreadcrumbsProvider, + BreadcrumbsChangeHandler, + Breadcrumb, + getDocTitle, +} from '../../components/breadcrumb'; +import { AuthenticationProvider } from '../../components/use_current_user'; import { tryDecodeURIComponent } from '../url_utils'; interface CreateParams { @@ -19,6 +29,10 @@ interface CreateParams { getStartServices: StartServicesAccessor; } +interface EditUserParams { + username: string; +} + export const usersManagementApp = Object.freeze({ id: 'users', create({ authc, getStartServices }: CreateParams) { @@ -27,18 +41,10 @@ export const usersManagementApp = Object.freeze({ order: 10, title: i18n.translate('xpack.security.management.usersTitle', { defaultMessage: 'Users' }), async mount({ element, setBreadcrumbs, history }) { - const [coreStart] = await getStartServices(); - const usersBreadcrumbs = [ - { - text: i18n.translate('xpack.security.users.breadcrumb', { defaultMessage: 'Users' }), - href: `/`, - }, - ]; - const [ - [{ http, notifications, i18n: i18nStart }], + [coreStart], { UsersGridPage }, - { EditUserPage }, + { CreateUserPage, EditUserPage }, { UserAPIClient }, { RolesAPIClient }, ] = await Promise.all([ @@ -49,64 +55,61 @@ export const usersManagementApp = Object.freeze({ import('../roles'), ]); - const userAPIClient = new UserAPIClient(http); - const rolesAPIClient = new RolesAPIClient(http); - const UsersGridPageWithBreadcrumbs = () => { - setBreadcrumbs(usersBreadcrumbs); - return ( - - ); - }; - - const EditUserPageWithBreadcrumbs = () => { - const { username } = useParams<{ username?: string }>(); - - // Additional decoding is a workaround for a bug in react-router's version of the `history` module. - // See https://github.com/elastic/kibana/issues/82440 - const decodedUsername = username ? tryDecodeURIComponent(username) : undefined; - - setBreadcrumbs([ - ...usersBreadcrumbs, - username - ? { text: decodedUsername, href: `/edit/${encodeURIComponent(username)}` } - : { - text: i18n.translate('xpack.security.users.createBreadcrumb', { - defaultMessage: 'Create', - }), - }, - ]); - - return ( - - ); - }; - render( - - + { + setBreadcrumbs(breadcrumbs); + coreStart.chrome.docTitle.change(getDocTitle(breadcrumbs)); + }} + > + - + - - + + + + + + ) => { + // Additional decoding is a workaround for a bug in react-router's version of the `history` module. + // See https://github.com/elastic/kibana/issues/82440 + const username = tryDecodeURIComponent(props.match.params.username); + return ( + + + + ); + }} + /> + + - - , + + , element ); @@ -117,3 +120,28 @@ export const usersManagementApp = Object.freeze({ } as RegisterManagementAppArgs; }, }); + +export interface ProvidersProps { + services: CoreStart; + history: History; + authc: AuthenticationServiceSetup; + onChange?: BreadcrumbsChangeHandler; +} + +export const Providers: FunctionComponent = ({ + services, + history, + authc, + onChange, + children, +}) => ( + + + + + {children} + + + + +); diff --git a/x-pack/plugins/security/server/routes/users/create_or_update.ts b/x-pack/plugins/security/server/routes/users/create_or_update.ts index a98848a583500..bdb6e89719037 100644 --- a/x-pack/plugins/security/server/routes/users/create_or_update.ts +++ b/x-pack/plugins/security/server/routes/users/create_or_update.ts @@ -30,12 +30,7 @@ export function defineCreateOrUpdateUserRoutes({ router }: RouteDefinitionParams try { await context.core.elasticsearch.client.asCurrentUser.security.putUser({ username: request.params.username, - // Omit `username`, `enabled` and all fields with `null` value. - body: Object.fromEntries( - Object.entries(request.body).filter( - ([key, value]) => value !== null && key !== 'enabled' && key !== 'username' - ) - ), + body: request.body, }); return response.ok({ body: request.body }); diff --git a/x-pack/plugins/security/server/routes/users/disable.ts b/x-pack/plugins/security/server/routes/users/disable.ts new file mode 100644 index 0000000000000..45e1f63149e1a --- /dev/null +++ b/x-pack/plugins/security/server/routes/users/disable.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema } from '@kbn/config-schema'; +import { RouteDefinitionParams } from '../index'; +import { wrapIntoCustomErrorResponse } from '../../errors'; +import { createLicensedRouteHandler } from '../licensed_route_handler'; + +export function defineDisableUserRoutes({ router }: RouteDefinitionParams) { + router.post( + { + path: '/internal/security/users/{username}/_disable', + validate: { + params: schema.object({ username: schema.string({ minLength: 1, maxLength: 1024 }) }), + }, + }, + createLicensedRouteHandler(async (context, request, response) => { + try { + await context.core.elasticsearch.client.asCurrentUser.security.disableUser({ + username: request.params.username, + }); + + return response.noContent(); + } catch (error) { + return response.customError(wrapIntoCustomErrorResponse(error)); + } + }) + ); +} diff --git a/x-pack/plugins/security/server/routes/users/enable.ts b/x-pack/plugins/security/server/routes/users/enable.ts new file mode 100644 index 0000000000000..0f4e15c953a42 --- /dev/null +++ b/x-pack/plugins/security/server/routes/users/enable.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema } from '@kbn/config-schema'; +import { RouteDefinitionParams } from '../index'; +import { wrapIntoCustomErrorResponse } from '../../errors'; +import { createLicensedRouteHandler } from '../licensed_route_handler'; + +export function defineEnableUserRoutes({ router }: RouteDefinitionParams) { + router.post( + { + path: '/internal/security/users/{username}/_enable', + validate: { + params: schema.object({ username: schema.string({ minLength: 1, maxLength: 1024 }) }), + }, + }, + createLicensedRouteHandler(async (context, request, response) => { + try { + await context.core.elasticsearch.client.asCurrentUser.security.enableUser({ + username: request.params.username, + }); + + return response.noContent(); + } catch (error) { + return response.customError(wrapIntoCustomErrorResponse(error)); + } + }) + ); +} diff --git a/x-pack/plugins/security/server/routes/users/index.ts b/x-pack/plugins/security/server/routes/users/index.ts index 931af0734b416..473b3459ad4e1 100644 --- a/x-pack/plugins/security/server/routes/users/index.ts +++ b/x-pack/plugins/security/server/routes/users/index.ts @@ -9,6 +9,8 @@ import { defineGetUserRoutes } from './get'; import { defineGetAllUsersRoutes } from './get_all'; import { defineCreateOrUpdateUserRoutes } from './create_or_update'; import { defineDeleteUserRoutes } from './delete'; +import { defineDisableUserRoutes } from './disable'; +import { defineEnableUserRoutes } from './enable'; import { defineChangeUserPasswordRoutes } from './change_password'; export function defineUsersRoutes(params: RouteDefinitionParams) { @@ -16,5 +18,7 @@ export function defineUsersRoutes(params: RouteDefinitionParams) { defineGetAllUsersRoutes(params); defineCreateOrUpdateUserRoutes(params); defineDeleteUserRoutes(params); + defineDisableUserRoutes(params); + defineEnableUserRoutes(params); defineChangeUserPasswordRoutes(params); } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 1bbf4b8033755..3a579adcf88c2 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -16894,7 +16894,7 @@ "xpack.security.management.editRole.updateRoleText": "ロールを更新", "xpack.security.management.editRole.validateRole.indicesTypeErrorMessage": "{elasticIndices} は数列でなければなりません", "xpack.security.management.editRole.validateRole.nameAllowedCharactersWarningMessage": "名前は文字またはアンダーラインで始まり、文字、アンダーライン、数字のみ使用できます。", - "xpack.security.management.editRole.validateRole.nameLengthWarningMessage": "名前は 1024 文字以内でなければなりません", + "xpack.security.management.editRole.validateRole.nameLengthWarningMessage": "名前は {maxLength} 文字以内でなければなりません", "xpack.security.management.editRole.validateRole.onePrivilegeRequiredWarningMessage": "権限が最低 1 つ必要です", "xpack.security.management.editRole.validateRole.oneSpaceRequiredWarningMessage": "スペースが最低 1 つ必要です", "xpack.security.management.editRole.validateRole.privilegeRequiredWarningMessage": "権限が必要です", @@ -17073,37 +17073,6 @@ "xpack.security.management.users.createNewUserButtonLabel": "ユーザーを作成", "xpack.security.management.users.deleteUsersButtonLabel": "{numSelected} 人のユーザー{numSelected, plural, one { } other {s}} 削除", "xpack.security.management.users.deniedPermissionTitle": "ユーザーを管理するにはパーミッションが必要です", - "xpack.security.management.users.editUser.addRolesPlaceholder": "ロールを追加", - "xpack.security.management.users.editUser.cancelButtonLabel": "キャンセル", - "xpack.security.management.users.editUser.changePasswordButtonLabel": "パスワードを変更", - "xpack.security.management.users.editUser.changePasswordExtraStepTitle": "追加ステップが必要です", - "xpack.security.management.users.editUser.changePasswordUpdateKibanaTitle": "{username}ユーザーのパスワードを変更後、{kibana}ファイルを更新し、Kibanaを再起動する必要があります。", - "xpack.security.management.users.editUser.changingUserNameAfterCreationDescription": "ユーザー名は作成後変更できません。", - "xpack.security.management.users.editUser.confirmPasswordFormRowLabel": "パスワードの確認", - "xpack.security.management.users.editUser.createUserButtonLabel": "ユーザーを作成", - "xpack.security.management.users.editUser.deleteUserButtonLabel": "ユーザーを削除", - "xpack.security.management.users.editUser.deprecatedRolesAssignedWarning": "このユーザーには非推奨ロールが割り当てられています。サポートされているロールに移行してください。", - "xpack.security.management.users.editUser.deprecatedRoleText": "(非推奨)", - "xpack.security.management.users.editUser.editUserTitle": "ユーザー {userName} の編集", - "xpack.security.management.users.editUser.emailAddressFormRowLabel": "メールアドレス", - "xpack.security.management.users.editUser.errorLoadingRolesTitle": "ロールの読み込み中にエラーが発生", - "xpack.security.management.users.editUser.errorLoadingUserTitle": "ユーザーの読み込み中にエラーが発生", - "xpack.security.management.users.editUser.fullNameFormRowLabel": "フルネーム", - "xpack.security.management.users.editUser.modifyingReservedUsersDescription": "リザーブされたユーザーはビルトインのため削除または変更できません。パスワードのみ変更できます。", - "xpack.security.management.users.editUser.newUserTitle": "新規ユーザー", - "xpack.security.management.users.editUser.passwordDoNotMatchErrorMessage": "パスワードが一致しません", - "xpack.security.management.users.editUser.passwordFormRowLabel": "パスワード", - "xpack.security.management.users.editUser.passwordLengthErrorMessage": "パスワードは最低 6 文字必要です", - "xpack.security.management.users.editUser.requiredUsernameErrorMessage": "ユーザー名が必要です", - "xpack.security.management.users.editUser.returnToUserListButtonLabel": "ユーザーリストに戻る", - "xpack.security.management.users.editUser.rolesFormRowLabel": "ロール", - "xpack.security.management.users.editUser.savingUserErrorMessage": "ユーザーの保存中にエラーが発生しました: {message}", - "xpack.security.management.users.editUser.settingPasswordErrorMessage": "パスワードの設定中にエラーが発生しました: {message}", - "xpack.security.management.users.editUser.updateUserButtonLabel": "ユーザーを更新", - "xpack.security.management.users.editUser.usernameAllowedCharactersErrorMessage": "ユーザー名は文字またはアンダーラインで始まり、文字、アンダーライン、数字のみ使用できます", - "xpack.security.management.users.editUser.usernameFormRowLabel": "ユーザー名", - "xpack.security.management.users.editUser.userSuccessfullySavedNotificationMessage": "保存されたユーザー {message}", - "xpack.security.management.users.editUser.validEmailRequiredErrorMessage": "メールアドレスが無効です", "xpack.security.management.users.emailAddressColumnName": "メールアドレス", "xpack.security.management.users.extendedUserDeprecationNotice": "{username}ユーザーは推奨されません。{reason}", "xpack.security.management.users.fetchingUsersErrorMessage": "ユーザーの取得中にエラーが発生: {message}", @@ -17140,7 +17109,6 @@ "xpack.security.roles.breadcrumb": "ロール", "xpack.security.roles.createBreadcrumb": "作成", "xpack.security.users.breadcrumb": "ユーザー", - "xpack.security.users.createBreadcrumb": "作成", "xpack.securitySolution.accessibility.tooltipWithKeyboardShortcut.pressTooltipLabel": "プレス", "xpack.securitySolution.add_filter_to_global_search_bar.filterForValueHoverAction": "値でフィルター", "xpack.securitySolution.add_filter_to_global_search_bar.filterOutValueHoverAction": "値を除外", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 51205a3420be5..9099fa0267ba9 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -16938,7 +16938,7 @@ "xpack.security.management.editRole.updateRoleText": "更新角色", "xpack.security.management.editRole.validateRole.indicesTypeErrorMessage": "{elasticIndices} 应为数组", "xpack.security.management.editRole.validateRole.nameAllowedCharactersWarningMessage": "名称必须以字母或下划线开头,且只能包含字母、下划线和数字。", - "xpack.security.management.editRole.validateRole.nameLengthWarningMessage": "名称不能超过 1024 个字符", + "xpack.security.management.editRole.validateRole.nameLengthWarningMessage": "名称不能超过 {maxLength} 个字符", "xpack.security.management.editRole.validateRole.onePrivilegeRequiredWarningMessage": "至少需要一个权限", "xpack.security.management.editRole.validateRole.oneSpaceRequiredWarningMessage": "至少需要一个工作区", "xpack.security.management.editRole.validateRole.privilegeRequiredWarningMessage": "“权限”必填", @@ -17117,37 +17117,6 @@ "xpack.security.management.users.createNewUserButtonLabel": "创建用户", "xpack.security.management.users.deleteUsersButtonLabel": "删除 {numSelected} 个用户{numSelected, plural, one { } other { 个用户}}", "xpack.security.management.users.deniedPermissionTitle": "您需要用于管理用户的权限", - "xpack.security.management.users.editUser.addRolesPlaceholder": "添加角色", - "xpack.security.management.users.editUser.cancelButtonLabel": "取消", - "xpack.security.management.users.editUser.changePasswordButtonLabel": "更改密码", - "xpack.security.management.users.editUser.changePasswordExtraStepTitle": "需要额外的步骤", - "xpack.security.management.users.editUser.changePasswordUpdateKibanaTitle": "更改 {username} 用户的密码后,必须更新 {kibana} 文件并重新启动 Kibana。", - "xpack.security.management.users.editUser.changingUserNameAfterCreationDescription": "用户名一经创建,将无法更改。", - "xpack.security.management.users.editUser.confirmPasswordFormRowLabel": "确认密码", - "xpack.security.management.users.editUser.createUserButtonLabel": "创建用户", - "xpack.security.management.users.editUser.deleteUserButtonLabel": "删除用户", - "xpack.security.management.users.editUser.deprecatedRolesAssignedWarning": "为此用户分配了过时的角色。请迁移到支持的角色。", - "xpack.security.management.users.editUser.deprecatedRoleText": "(已过时)", - "xpack.security.management.users.editUser.editUserTitle": "编辑 {userName} 用户", - "xpack.security.management.users.editUser.emailAddressFormRowLabel": "电子邮件地址", - "xpack.security.management.users.editUser.errorLoadingRolesTitle": "加载角色时出错", - "xpack.security.management.users.editUser.errorLoadingUserTitle": "加载用户时出错", - "xpack.security.management.users.editUser.fullNameFormRowLabel": "全名", - "xpack.security.management.users.editUser.modifyingReservedUsersDescription": "保留用户是内置用户,无法删除或修改。只能更改密码。", - "xpack.security.management.users.editUser.newUserTitle": "新建用户", - "xpack.security.management.users.editUser.passwordDoNotMatchErrorMessage": "密码不匹配", - "xpack.security.management.users.editUser.passwordFormRowLabel": "密码", - "xpack.security.management.users.editUser.passwordLengthErrorMessage": "密码长度必须至少为 6 个字符", - "xpack.security.management.users.editUser.requiredUsernameErrorMessage": "“用户名”必填", - "xpack.security.management.users.editUser.returnToUserListButtonLabel": "返回到用户列表", - "xpack.security.management.users.editUser.rolesFormRowLabel": "角色", - "xpack.security.management.users.editUser.savingUserErrorMessage": "保存用户时出错:{message}", - "xpack.security.management.users.editUser.settingPasswordErrorMessage": "设置密码时出错:{message}", - "xpack.security.management.users.editUser.updateUserButtonLabel": "更新用户", - "xpack.security.management.users.editUser.usernameAllowedCharactersErrorMessage": "用户名必须以字母或下划线开头,并只能包含字母、下划线和数字", - "xpack.security.management.users.editUser.usernameFormRowLabel": "用户名", - "xpack.security.management.users.editUser.userSuccessfullySavedNotificationMessage": "已保存用户{message}", - "xpack.security.management.users.editUser.validEmailRequiredErrorMessage": "电子邮件地址无效", "xpack.security.management.users.emailAddressColumnName": "电子邮件地址", "xpack.security.management.users.extendedUserDeprecationNotice": "用户 {username} 已过时。{reason}", "xpack.security.management.users.fetchingUsersErrorMessage": "提取用户时出错:{message}", @@ -17184,7 +17153,6 @@ "xpack.security.roles.breadcrumb": "角色", "xpack.security.roles.createBreadcrumb": "创建", "xpack.security.users.breadcrumb": "用户", - "xpack.security.users.createBreadcrumb": "创建", "xpack.securitySolution.accessibility.tooltipWithKeyboardShortcut.pressTooltipLabel": "按", "xpack.securitySolution.add_filter_to_global_search_bar.filterForValueHoverAction": "筛留值", "xpack.securitySolution.add_filter_to_global_search_bar.filterOutValueHoverAction": "筛除值", diff --git a/x-pack/test/accessibility/apps/users.ts b/x-pack/test/accessibility/apps/users.ts index efdcf4f3f022f..ede120ca43de7 100644 --- a/x-pack/test/accessibility/apps/users.ts +++ b/x-pack/test/accessibility/apps/users.ts @@ -13,6 +13,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); const esArchiver = getService('esArchiver'); const testSubjects = getService('testSubjects'); + const find = getService('find'); const retry = getService('retry'); describe('Kibana users page a11y tests', () => { @@ -52,24 +53,29 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('a11y test for roles drop down', async () => { - await testSubjects.setValue('userFormUserNameInput', 'a11y'); - await testSubjects.setValue('passwordInput', 'password'); - await testSubjects.setValue('passwordConfirmationInput', 'password'); - await testSubjects.setValue('userFormFullNameInput', 'a11y user'); - await testSubjects.setValue('userFormEmailInput', 'example@example.com'); + await PageObjects.security.clickElasticsearchUsers(); + await PageObjects.security.clickCreateNewUser(); + await PageObjects.security.fillUserForm({ + username: 'a11y', + password: 'password', + confirm_password: 'password', + full_name: 'a11y user', + email: 'example@example.com', + roles: ['apm_user'], + }); await testSubjects.click('rolesDropdown'); await a11y.testAppSnapshot(); }); - it('a11y test for display of delete button on users page ', async () => { - await testSubjects.setValue('userFormUserNameInput', 'deleteA11y'); - await testSubjects.setValue('passwordInput', 'password'); - await testSubjects.setValue('passwordConfirmationInput', 'password'); - await testSubjects.setValue('userFormFullNameInput', 'DeleteA11y user'); - await testSubjects.setValue('userFormEmailInput', 'example@example.com'); - await testSubjects.click('rolesDropdown'); - await testSubjects.setValue('rolesDropdown', 'roleOption-apm_user'); - await testSubjects.click('userFormSaveButton'); + it('a11y test for display of delete button on users page', async () => { + await PageObjects.security.createUser({ + username: 'deleteA11y', + password: 'password', + confirm_password: 'password', + full_name: 'DeleteA11y user', + email: 'example@example.com', + roles: ['apm_user'], + }); await testSubjects.click('checkboxSelectRow-deleteA11y'); await a11y.testAppSnapshot(); }); @@ -77,17 +83,24 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('a11y test for delete user panel ', async () => { await testSubjects.click('deleteUserButton'); await a11y.testAppSnapshot(); + await testSubjects.click('confirmModalCancelButton'); }); it('a11y test for edit user panel', async () => { - await testSubjects.click('confirmModalCancelButton'); await PageObjects.settings.clickLinkText('deleteA11y'); await a11y.testAppSnapshot(); }); - it('a11y test for Change password screen', async () => { + it('a11y test for change password screen', async () => { + await PageObjects.settings.clickLinkText('deleteA11y'); + await find.clickByButtonText('Change password'); + await a11y.testAppSnapshot(); + await testSubjects.click('formFlyoutCancelButton'); + }); + + it('a11y test for deactivate user screen', async () => { await PageObjects.settings.clickLinkText('deleteA11y'); - await testSubjects.click('changePassword'); + await find.clickByButtonText('Deactivate user'); await a11y.testAppSnapshot(); }); }); diff --git a/x-pack/test/api_integration/apis/security/index.ts b/x-pack/test/api_integration/apis/security/index.ts index 2d112215f4fc1..9084e635f8109 100644 --- a/x-pack/test/api_integration/apis/security/index.ts +++ b/x-pack/test/api_integration/apis/security/index.ts @@ -19,6 +19,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./change_password')); loadTestFile(require.resolve('./index_fields')); loadTestFile(require.resolve('./roles')); + loadTestFile(require.resolve('./users')); loadTestFile(require.resolve('./privileges')); }); } diff --git a/x-pack/test/api_integration/apis/security/security_basic.ts b/x-pack/test/api_integration/apis/security/security_basic.ts index 191523e969717..6872f423fe630 100644 --- a/x-pack/test/api_integration/apis/security/security_basic.ts +++ b/x-pack/test/api_integration/apis/security/security_basic.ts @@ -19,6 +19,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./change_password')); loadTestFile(require.resolve('./index_fields')); loadTestFile(require.resolve('./roles')); + loadTestFile(require.resolve('./users')); loadTestFile(require.resolve('./privileges_basic')); }); } diff --git a/x-pack/test/api_integration/apis/security/users.ts b/x-pack/test/api_integration/apis/security/users.ts new file mode 100644 index 0000000000000..e177cf998beee --- /dev/null +++ b/x-pack/test/api_integration/apis/security/users.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const security = getService('security'); + const es = getService('es'); + + const mockUserName = 'test-user'; + const mockUserPassword = 'test-password'; + + describe('Users', () => { + beforeEach(async () => { + await security.user.create(mockUserName, { password: mockUserPassword, roles: [] }); + }); + + afterEach(async () => { + await security.user.delete(mockUserName); + }); + + it('should disable user', async () => { + await supertest + .post(`/internal/security/users/${mockUserName}/_disable`) + .set('kbn-xsrf', 'xxx') + .expect(204); + + const { body } = await es.security.getUser({ username: mockUserName }); + expect(body[mockUserName].enabled).to.be(false); + }); + + it('should enable user', async () => { + await supertest + .post(`/internal/security/users/${mockUserName}/_enable`) + .set('kbn-xsrf', 'xxx') + .expect(204); + + const { body } = await es.security.getUser({ username: mockUserName }); + expect(body[mockUserName].enabled).to.be(true); + }); + }); +} diff --git a/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js b/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js index 00183113a4d59..b2ddf7d47b1f1 100644 --- a/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js +++ b/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js @@ -68,46 +68,36 @@ export default function ({ getService, getPageObjects }) { before('Create dashboard only mode user', async () => { await PageObjects.settings.navigateTo(); - await PageObjects.security.clickUsersSection(); - await PageObjects.security.clickCreateNewUser(); - await testSubjects.setValue('userFormUserNameInput', 'dashuser'); - await testSubjects.setValue('passwordInput', '123456'); - await testSubjects.setValue('passwordConfirmationInput', '123456'); - await testSubjects.setValue('userFormFullNameInput', 'dashuser'); - await testSubjects.setValue('userFormEmailInput', 'example@example.com'); - await PageObjects.security.assignRoleToUser('kibana_dashboard_only_user'); - await PageObjects.security.assignRoleToUser('logstash-data'); - - await PageObjects.security.clickSaveEditUser(); + await PageObjects.security.createUser({ + username: 'dashuser', + password: '123456', + confirm_password: '123456', + email: 'example@example.com', + full_name: 'dashuser', + roles: ['kibana_dashboard_only_user', 'logstash-data'], + }); }); before('Create user with mixes roles', async () => { - await PageObjects.security.clickCreateNewUser(); - - await testSubjects.setValue('userFormUserNameInput', 'mixeduser'); - await testSubjects.setValue('passwordInput', '123456'); - await testSubjects.setValue('passwordConfirmationInput', '123456'); - await testSubjects.setValue('userFormFullNameInput', 'mixeduser'); - await testSubjects.setValue('userFormEmailInput', 'example@example.com'); - await PageObjects.security.assignRoleToUser('kibana_dashboard_only_user'); - await PageObjects.security.assignRoleToUser('kibana_admin'); - await PageObjects.security.assignRoleToUser('logstash-data'); - - await PageObjects.security.clickSaveEditUser(); + await PageObjects.security.createUser({ + username: 'mixeduser', + password: '123456', + confirm_password: '123456', + email: 'example@example.com', + full_name: 'mixeduser', + roles: ['kibana_dashboard_only_user', 'kibana_admin', 'logstash-data'], + }); }); before('Create user with dashboard and superuser role', async () => { - await PageObjects.security.clickCreateNewUser(); - - await testSubjects.setValue('userFormUserNameInput', 'mysuperuser'); - await testSubjects.setValue('passwordInput', '123456'); - await testSubjects.setValue('passwordConfirmationInput', '123456'); - await testSubjects.setValue('userFormFullNameInput', 'mixeduser'); - await testSubjects.setValue('userFormEmailInput', 'example@example.com'); - await PageObjects.security.assignRoleToUser('kibana_dashboard_only_user'); - await PageObjects.security.assignRoleToUser('superuser'); - - await PageObjects.security.clickSaveEditUser(); + await PageObjects.security.createUser({ + username: 'mysuperuser', + password: '123456', + confirm_password: '123456', + email: 'example@example.com', + full_name: 'mixeduser', + roles: ['kibana_dashboard_only_user', 'superuser'], + }); }); after(async () => { diff --git a/x-pack/test/functional/apps/security/doc_level_security_roles.js b/x-pack/test/functional/apps/security/doc_level_security_roles.js index 0595322ad2d21..a76475fbbbd8c 100644 --- a/x-pack/test/functional/apps/security/doc_level_security_roles.js +++ b/x-pack/test/functional/apps/security/doc_level_security_roles.js @@ -51,14 +51,12 @@ export default function ({ getService, getPageObjects }) { }); it('should add new user userEAST ', async function () { - await PageObjects.security.clickElasticsearchUsers(); - await PageObjects.security.addUser({ + await PageObjects.security.createUser({ username: 'userEast', password: 'changeme', - confirmPassword: 'changeme', - fullname: 'dls EAST', + confirm_password: 'changeme', + full_name: 'dls EAST', email: 'dlstest@elastic.com', - save: true, roles: ['kibana_admin', 'myroleEast'], }); const users = keyBy(await PageObjects.security.getElasticsearchUsers(), 'username'); diff --git a/x-pack/test/functional/apps/security/field_level_security.js b/x-pack/test/functional/apps/security/field_level_security.js index 3f3984dd05a94..a4e2680c394ee 100644 --- a/x-pack/test/functional/apps/security/field_level_security.js +++ b/x-pack/test/functional/apps/security/field_level_security.js @@ -71,14 +71,12 @@ export default function ({ getService, getPageObjects }) { }); it('should add new user customer1 ', async function () { - await PageObjects.security.clickElasticsearchUsers(); - await PageObjects.security.addUser({ + await PageObjects.security.createUser({ username: 'customer1', password: 'changeme', - confirmPassword: 'changeme', - fullname: 'customer one', + confirm_password: 'changeme', + full_name: 'customer one', email: 'flstest@elastic.com', - save: true, roles: ['kibana_admin', 'a_viewssnrole'], }); const users = keyBy(await PageObjects.security.getElasticsearchUsers(), 'username'); @@ -87,14 +85,12 @@ export default function ({ getService, getPageObjects }) { }); it('should add new user customer2 ', async function () { - await PageObjects.security.clickElasticsearchUsers(); - await PageObjects.security.addUser({ + await PageObjects.security.createUser({ username: 'customer2', password: 'changeme', - confirmPassword: 'changeme', - fullname: 'customer two', + confirm_password: 'changeme', + full_name: 'customer two', email: 'flstest@elastic.com', - save: true, roles: ['kibana_admin', 'a_view_no_ssn_role'], }); const users = keyBy(await PageObjects.security.getElasticsearchUsers(), 'username'); diff --git a/x-pack/test/functional/apps/security/rbac_phase1.js b/x-pack/test/functional/apps/security/rbac_phase1.js index 58c72eaa3072e..de4515c501187 100644 --- a/x-pack/test/functional/apps/security/rbac_phase1.js +++ b/x-pack/test/functional/apps/security/rbac_phase1.js @@ -58,13 +58,12 @@ export default function ({ getService, getPageObjects }) { ], }, }); - await PageObjects.security.clickElasticsearchUsers(); log.debug('After Add user new: , userObj.userName'); - await PageObjects.security.addUser({ + await PageObjects.security.createUser({ username: 'kibanauser', password: 'changeme', - confirmPassword: 'changeme', - fullname: 'kibanafirst kibanalast', + confirm_password: 'changeme', + full_name: 'kibanafirst kibanalast', email: 'kibanauser@myEmail.com', save: true, roles: ['rbac_all'], @@ -76,13 +75,12 @@ export default function ({ getService, getPageObjects }) { expect(users.kibanauser.roles).to.eql(['rbac_all']); expect(users.kibanauser.fullname).to.eql('kibanafirst kibanalast'); expect(users.kibanauser.reserved).to.be(false); - await PageObjects.security.clickElasticsearchUsers(); log.debug('After Add user new: , userObj.userName'); - await PageObjects.security.addUser({ + await PageObjects.security.createUser({ username: 'kibanareadonly', password: 'changeme', - confirmPassword: 'changeme', - fullname: 'kibanareadonlyFirst kibanareadonlyLast', + confirm_password: 'changeme', + full_name: 'kibanareadonlyFirst kibanareadonlyLast', email: 'kibanareadonly@myEmail.com', save: true, roles: ['rbac_read'], diff --git a/x-pack/test/functional/apps/security/role_mappings.ts b/x-pack/test/functional/apps/security/role_mappings.ts index 96f16aebd11b9..6f76367801536 100644 --- a/x-pack/test/functional/apps/security/role_mappings.ts +++ b/x-pack/test/functional/apps/security/role_mappings.ts @@ -9,7 +9,7 @@ import { parse } from 'url'; import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ getPageObjects, getService }: FtrProviderContext) => { - const pageObjects = getPageObjects(['common', 'roleMappings']); + const pageObjects = getPageObjects(['common', 'security', 'roleMappings']); const security = getService('security'); const testSubjects = getService('testSubjects'); const browser = getService('browser'); @@ -32,8 +32,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('allows a role mapping to be created', async () => { await testSubjects.click('createRoleMappingButton'); await testSubjects.setValue('roleMappingFormNameInput', 'new_role_mapping'); - await testSubjects.setValue('rolesDropdown', 'superuser'); - await browser.pressKeys(browser.keys.ENTER); + await pageObjects.security.selectRole('superuser'); await testSubjects.click('roleMappingsAddRuleButton'); diff --git a/x-pack/test/functional/apps/security/secure_roles_perm.js b/x-pack/test/functional/apps/security/secure_roles_perm.js index c547657bf880a..830d8384f1e3d 100644 --- a/x-pack/test/functional/apps/security/secure_roles_perm.js +++ b/x-pack/test/functional/apps/security/secure_roles_perm.js @@ -51,15 +51,13 @@ export default function ({ getService, getPageObjects }) { }); it('should add new user', async function () { - await PageObjects.security.clickElasticsearchUsers(); log.debug('After Add user new: , userObj.userName'); - await PageObjects.security.addUser({ + await PageObjects.security.createUser({ username: 'Rashmi', password: 'changeme', - confirmPassword: 'changeme', - fullname: 'RashmiFirst RashmiLast', + confirm_password: 'changeme', + full_name: 'RashmiFirst RashmiLast', email: 'rashmi@myEmail.com', - save: true, roles: ['logstash_reader', 'kibana_admin'], }); log.debug('After Add user: , userObj.userName'); diff --git a/x-pack/test/functional/apps/security/user_email.js b/x-pack/test/functional/apps/security/user_email.js index a2a2b705172d7..c05220b6a59f3 100644 --- a/x-pack/test/functional/apps/security/user_email.js +++ b/x-pack/test/functional/apps/security/user_email.js @@ -19,13 +19,12 @@ export default function ({ getService, getPageObjects }) { }); it('should add new user', async function () { - await PageObjects.security.addUser({ + await PageObjects.security.createUser({ username: 'newuser', password: 'changeme', - confirmPassword: 'changeme', - fullname: 'newuserFirst newuserLast', + confirm_password: 'changeme', + full_name: 'newuserFirst newuserLast', email: 'newuser@myEmail.com', - save: true, roles: ['kibana_admin', 'superuser'], }); const users = keyBy(await PageObjects.security.getElasticsearchUsers(), 'username'); diff --git a/x-pack/test/functional/apps/security/users.js b/x-pack/test/functional/apps/security/users.js index 4fd4384a93c59..7f2b0cfd96ca2 100644 --- a/x-pack/test/functional/apps/security/users.js +++ b/x-pack/test/functional/apps/security/users.js @@ -41,13 +41,12 @@ export default function ({ getService, getPageObjects }) { }); it('should add new user', async function () { - await PageObjects.security.addUser({ + await PageObjects.security.createUser({ username: 'Lee', password: 'LeePwd', - confirmPassword: 'LeePwd', - fullname: 'LeeFirst LeeLast', + confirm_password: 'LeePwd', + full_name: 'LeeFirst LeeLast', email: 'lee@myEmail.com', - save: true, roles: ['kibana_admin'], }); const users = keyBy(await PageObjects.security.getElasticsearchUsers(), 'username'); @@ -59,11 +58,10 @@ export default function ({ getService, getPageObjects }) { }); it('should add new user with optional fields left empty', async function () { - await PageObjects.security.addUser({ + await PageObjects.security.createUser({ username: 'OptionalUser', password: 'OptionalUserPwd', - confirmPassword: 'OptionalUserPwd', - save: true, + confirm_password: 'OptionalUserPwd', roles: [], }); const users = keyBy(await PageObjects.security.getElasticsearchUsers(), 'username'); diff --git a/x-pack/test/functional/page_objects/security_page.ts b/x-pack/test/functional/page_objects/security_page.ts index cad5e29528e9c..868d8115e7f0f 100644 --- a/x-pack/test/functional/page_objects/security_page.ts +++ b/x-pack/test/functional/page_objects/security_page.ts @@ -7,6 +7,7 @@ import { adminTestUser } from '@kbn/test'; import { FtrProviderContext } from '../ftr_provider_context'; import { AuthenticatedUser, Role } from '../../../plugins/security/common/model'; +import type { UserFormValues } from '../../../plugins/security/public/management/users/edit_user/user_form'; export function SecurityPageProvider({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); @@ -275,7 +276,7 @@ export function SecurityPageProvider({ getService, getPageObjects }: FtrProvider } async clickCancelEditUser() { - await testSubjects.click('userFormCancelButton'); + await find.clickByButtonText('Cancel'); } async clickCancelEditRole() { @@ -283,7 +284,7 @@ export function SecurityPageProvider({ getService, getPageObjects }: FtrProvider } async clickSaveEditUser() { - await testSubjects.click('userFormSaveButton'); + await find.clickByButtonText('Update user'); await PageObjects.header.waitUntilLoadingHasFinished(); } @@ -380,53 +381,58 @@ export function SecurityPageProvider({ getService, getPageObjects }: FtrProvider return roles; } + /** + * @deprecated Use `PageObjects.security.clickCreateNewUser` instead + */ async clickNewUser() { return await testSubjects.click('createUserButton'); } + /** + * @deprecated Use `PageObjects.security.clickCreateNewUser` instead + */ async clickNewRole() { return await testSubjects.click('createRoleButton'); } - async addUser(userObj: { - username: string; - password: string; - confirmPassword: string; - email: string; - fullname: string; - roles: string[]; - save?: boolean; - }) { - const self = this; - await this.clickNewUser(); - log.debug('username = ' + userObj.username); - await testSubjects.setValue('userFormUserNameInput', userObj.username); - await testSubjects.setValue('passwordInput', userObj.password); - await testSubjects.setValue('passwordConfirmationInput', userObj.confirmPassword); - if (userObj.fullname) { - await testSubjects.setValue('userFormFullNameInput', userObj.fullname); + async fillUserForm(user: UserFormValues) { + if (user.username) { + await find.setValue('[name=username]', user.username); + } + if (user.password) { + await find.setValue('[name=password]', user.password); + } + if (user.confirm_password) { + await find.setValue('[name=confirm_password]', user.confirm_password); } - if (userObj.email) { - await testSubjects.setValue('userFormEmailInput', userObj.email); + if (user.full_name) { + await find.setValue('[name=full_name]', user.full_name); + } + if (user.email) { + await find.setValue('[name=email]', user.email); } - log.debug('Add roles: ', userObj.roles); - const rolesToAdd = userObj.roles || []; + const rolesToAdd = user.roles || []; for (let i = 0; i < rolesToAdd.length; i++) { - await self.selectRole(rolesToAdd[i]); - } - log.debug('After Add role: , userObj.roleName'); - if (userObj.save === true) { - await testSubjects.click('userFormSaveButton'); - } else { - await testSubjects.click('userFormCancelButton'); + await this.selectRole(rolesToAdd[i]); } } + async submitCreateUserForm() { + await find.clickByButtonText('Create user'); + } + + async createUser(user: UserFormValues) { + await this.clickElasticsearchUsers(); + await this.clickCreateNewUser(); + await this.fillUserForm(user); + await this.submitCreateUserForm(); + } + async addRole(roleName: string, roleObj: Role) { const self = this; - await this.clickNewRole(); + await this.clickCreateNewRole(); // We have to use non-test-subject selectors because this markup is generated by ui-select. log.debug('roleObj.indices[0].names = ' + roleObj.elasticsearch.indices[0].names); @@ -498,37 +504,23 @@ export function SecurityPageProvider({ getService, getPageObjects }: FtrProvider const dropdown = await testSubjects.find('rolesDropdown'); const input = await dropdown.findByCssSelector('input'); await input.type(role); - await testSubjects.click(`roleOption-${role}`); + await find.clickByCssSelector(`[role=option][title="${role}"]`); await testSubjects.click('comboBoxToggleListButton'); - await testSubjects.find(`roleOption-${role}`); } - deleteUser(username: string) { - let alertText: string; + async deleteUser(username: string) { log.debug('Delete user ' + username); - return find - .clickByDisplayedLinkText(username) - .then(() => { - return PageObjects.header.awaitGlobalLoadingIndicatorHidden(); - }) - .then(() => { - log.debug('Find delete button and click'); - return testSubjects.click('userFormDeleteButton'); - }) - .then(() => { - return PageObjects.common.sleep(2000); - }) - .then(() => { - return testSubjects.getVisibleText('confirmModalBodyText'); - }) - .then((alert) => { - alertText = alert; - log.debug('Delete user alert text = ' + alertText); - return testSubjects.click('confirmModalConfirmButton'); - }) - .then(() => { - return alertText; - }); + await find.clickByDisplayedLinkText(username); + await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); + + log.debug('Find delete button and click'); + await find.clickByButtonText('Delete user'); + await PageObjects.common.sleep(2000); + + const confirmText = await testSubjects.getVisibleText('confirmModalBodyText'); + log.debug('Delete user alert text = ' + confirmText); + await testSubjects.click('confirmModalConfirmButton'); + return confirmText; } } return new SecurityPage(); diff --git a/yarn.lock b/yarn.lock index 828a3b630a838..e7870415b0dda 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5137,10 +5137,10 @@ dependencies: "@types/sizzle" "*" -"@types/js-cookie@2.2.5": - version "2.2.5" - resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.5.tgz#38dfaacae8623b37cc0b0d27398e574e3fc28b1e" - integrity sha512-cpmwBRcHJmmZx0OGU7aPVwGWGbs4iKwVYchk9iuMtxNCA2zorwdaTz4GkLgs2WGxiRZRFKnV1k6tRUHX7tBMxg== +"@types/js-cookie@2.2.6": + version "2.2.6" + resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.6.tgz#f1a1cb35aff47bc5cfb05cb0c441ca91e914c26f" + integrity sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw== "@types/js-search@^1.4.0": version "1.4.0" @@ -6374,10 +6374,10 @@ dependencies: tslib "^1.9.3" -"@xobotyi/scrollbar-width@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.4.tgz#a7dce20b7465bcad29cd6bbb557695e4ea7863cb" - integrity sha512-o12FCQt/X5n3pgKEWGpt0f/7Eg4mfv3uRwPUrctiOT8ZuxbH3cNLGWfH/8y6KxVJg4L2885ucuXQ6XECZzUiJA== +"@xobotyi/scrollbar-width@1.9.5": + version "1.9.5" + resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" + integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -13368,7 +13368,7 @@ fancy-log@^1.3.2: color-support "^1.1.3" time-stamp "^1.0.0" -fast-deep-equal@^3.1.1, fast-deep-equal@~3.1.3: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3, fast-deep-equal@~3.1.3: version "3.1.1" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== @@ -23597,24 +23597,30 @@ react-transition-group@^4.3.0: loose-envify "^1.4.0" prop-types "^15.6.2" -react-use@^13.27.0: - version "13.27.0" - resolved "https://registry.yarnpkg.com/react-use/-/react-use-13.27.0.tgz#53a619dc9213e2cbe65d6262e8b0e76641ade4aa" - integrity sha512-2lyTyqJWyvnaP/woVtDcFS4B5pUYz0FQWI9pVHk/6TBWom2x3/ziJthkEn/LbCA9Twv39xSQU7Dn0zdIWfsNTQ== +react-universal-interface@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b" + integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw== + +react-use@^15.3.4: + version "15.3.4" + resolved "https://registry.yarnpkg.com/react-use/-/react-use-15.3.4.tgz#f853d310bd71f75b38900a8caa3db93f6dc6e872" + integrity sha512-cHq1dELW6122oi1+xX7lwNyE/ugZs5L902BuO8eFJCfn2api1KeuPVG1M/GJouVARoUf54S2dYFMKo5nQXdTag== dependencies: - "@types/js-cookie" "2.2.5" - "@xobotyi/scrollbar-width" "1.9.4" + "@types/js-cookie" "2.2.6" + "@xobotyi/scrollbar-width" "1.9.5" copy-to-clipboard "^3.2.0" - fast-deep-equal "^3.1.1" + fast-deep-equal "^3.1.3" fast-shallow-equal "^1.0.0" js-cookie "^2.2.1" nano-css "^5.2.1" + react-universal-interface "^0.6.2" resize-observer-polyfill "^1.5.1" screenfull "^5.0.0" set-harmonic-interval "^1.0.1" throttle-debounce "^2.1.0" ts-easing "^0.2.0" - tslib "^1.10.0" + tslib "^2.0.0" react-virtualized-auto-sizer@^1.0.2: version "1.0.2" From e8f338e78c47969801d6de9f7822f7020eecc34c Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 25 Jan 2021 14:56:55 +0100 Subject: [PATCH 25/62] [Uptime] Added View performance breakdown button (#88658) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/common/step_detail_link.tsx | 17 ++++---- .../monitor/synthetics/executed_step.tsx | 42 ++++++++----------- .../monitor/synthetics/translations.ts | 14 +++++++ 3 files changed, 42 insertions(+), 31 deletions(-) create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/translations.ts diff --git a/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx b/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx index a8e4c90f2d29a..886496a7f6e2f 100644 --- a/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx +++ b/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx @@ -5,8 +5,7 @@ */ import React, { FC } from 'react'; -import { EuiLink } from '@elastic/eui'; -import { Link } from 'react-router-dom'; +import { ReactRouterEuiButton } from './react_router_helpers'; interface StepDetailLinkProps { /** @@ -23,10 +22,14 @@ export const StepDetailLink: FC = ({ children, checkGroupId const to = `/journey/${checkGroupId}/step/${stepIndex}`; return ( - - - {children} - - + + {children} + ); }; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx index 01a599f8e8a60..934427643757d 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx @@ -13,6 +13,7 @@ import { StepScreenshotDisplay } from './step_screenshot_display'; import { StatusBadge } from './status_badge'; import { Ping } from '../../../../common/runtime_types'; import { StepDetailLink } from '../../common/step_detail_link'; +import { VIEW_PERFORMANCE } from './translations'; const CODE_BLOCK_OVERFLOW_HEIGHT = 360; @@ -26,24 +27,9 @@ export const ExecutedStep: FC = ({ step, index, checkGroup }) return ( <>
-
- {step.synthetics?.step?.index && checkGroup ? ( - - - - - - - - ) : ( - + + + = ({ step, index, checkGroup }) /> - )} -
- -
- -
+ +
+ +
+ +
@@ -73,6 +59,14 @@ export const ExecutedStep: FC = ({ step, index, checkGroup }) /> + {step.synthetics?.step?.index && ( + + + {VIEW_PERFORMANCE} + + + + )} Date: Mon, 25 Jan 2021 15:01:29 +0100 Subject: [PATCH 26/62] Ignore missing asset errors on remove. (#89115) --- x-pack/plugins/fleet/server/services/epm/packages/remove.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/services/epm/packages/remove.ts b/x-pack/plugins/fleet/server/services/epm/packages/remove.ts index 331b6bfa882da..94e81e296b5a9 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/remove.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/remove.ts @@ -115,7 +115,10 @@ async function deleteAssets( try { await Promise.all(deletePromises); } catch (err) { - logger.error(err); + // in the rollback case, partial installs are likely, so missing assets are not an error + if (!savedObjectsClient.errors.isNotFoundError(err)) { + logger.error(err); + } } } From 93c46f5dfc88b74cd3b2790588aa457ac55bfedf Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Mon, 25 Jan 2021 15:29:10 +0100 Subject: [PATCH 27/62] Remove tag name validation (#88800) * Remove tag name validation * remove i18n key * add FTR test on searching for tag with special chars in name --- docs/management/managing-tags.asciidoc | 3 +- .../common/validation.test.ts | 6 +- .../common/validation.ts | 6 -- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../global_search/search_syntax/data.json | 56 +++++++++++++++++++ .../global_search/global_search_bar.ts | 13 ++++- .../tagging_api/apis/create.ts | 4 +- .../tagging_api/apis/update.ts | 4 +- .../functional/tests/create.ts | 4 +- .../functional/tests/edit.ts | 4 +- 11 files changed, 79 insertions(+), 23 deletions(-) diff --git a/docs/management/managing-tags.asciidoc b/docs/management/managing-tags.asciidoc index 3da98b2281fdc..88fdef66a7418 100644 --- a/docs/management/managing-tags.asciidoc +++ b/docs/management/managing-tags.asciidoc @@ -37,8 +37,7 @@ Create a tag to assign to your saved objects. image::images/tags/create-tag.png[Tag creation popin] . Enter a name and select a color for the new tag. + -The name can include alphanumeric characters (English letters and digits), `:`, `-`, `_` and the space character, -and cannot be longer than 50 characters. +The name cannot be longer than 50 characters. . Click *Create tag*. [float] diff --git a/x-pack/plugins/saved_objects_tagging/common/validation.test.ts b/x-pack/plugins/saved_objects_tagging/common/validation.test.ts index 232387e964cbf..a601a96a49e75 100644 --- a/x-pack/plugins/saved_objects_tagging/common/validation.test.ts +++ b/x-pack/plugins/saved_objects_tagging/common/validation.test.ts @@ -20,10 +20,8 @@ describe('Tag attributes validation', () => { ); }); - it('returns an error message if the name contains invalid characters', () => { - expect(validateTagName('t^ag+name&')).toMatchInlineSnapshot( - `"Tag name can only include a-z, 0-9, _, -,:."` - ); + it('does not return an error message if the name contains special characters', () => { + expect(validateTagName('t^ag+name&')).toBeUndefined(); }); }); diff --git a/x-pack/plugins/saved_objects_tagging/common/validation.ts b/x-pack/plugins/saved_objects_tagging/common/validation.ts index 12149d7bdbe79..5cb9e068516fe 100644 --- a/x-pack/plugins/saved_objects_tagging/common/validation.ts +++ b/x-pack/plugins/saved_objects_tagging/common/validation.ts @@ -12,7 +12,6 @@ export const tagNameMaxLength = 50; export const tagDescriptionMaxLength = 100; const hexColorRegexp = /^#[0-9A-F]{6}$/i; -const nameValidCharsRegexp = /^[0-9A-Z:\-_\s]+$/i; export interface TagValidation { valid: boolean; @@ -49,11 +48,6 @@ export const validateTagName = (name: string): string | undefined => { }, }); } - if (!nameValidCharsRegexp.test(name)) { - return i18n.translate('xpack.savedObjectsTagging.validation.name.errorInvalidCharacters', { - defaultMessage: 'Tag name can only include a-z, 0-9, _, -,:.', - }); - } }; export const validateTagDescription = (description: string): string | undefined => { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 3a579adcf88c2..ef2149c4931fa 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -16633,7 +16633,6 @@ "xpack.savedObjectsTagging.uiApi.table.columnTagsName": "タグ", "xpack.savedObjectsTagging.validation.color.errorInvalid": "タグ色は有効な 16 進数値色でなければなりません", "xpack.savedObjectsTagging.validation.description.errorTooLong": "タグ説明は {length} 文字以下で入力してください", - "xpack.savedObjectsTagging.validation.name.errorInvalidCharacters": "タグ名には、a-z、0-9、-、: のみを使用できます。", "xpack.savedObjectsTagging.validation.name.errorTooLong": "タグ名は {length} 文字以下で入力してください", "xpack.savedObjectsTagging.validation.name.errorTooShort": "タグ名は {length} 文字以上で入力してください", "xpack.searchProfiler.advanceTimeDescription": "イテレーターを次のドキュメントに進めるためにかかった時間。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 9099fa0267ba9..08d064ce8a05c 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -16675,7 +16675,6 @@ "xpack.savedObjectsTagging.uiApi.table.columnTagsName": "标签", "xpack.savedObjectsTagging.validation.color.errorInvalid": "标签颜色必须为有效的十六进制颜色", "xpack.savedObjectsTagging.validation.description.errorTooLong": "标签描述不能超过 {length} 个字符。", - "xpack.savedObjectsTagging.validation.name.errorInvalidCharacters": "标签名称只能包含 a-z、0-9、_、-、:。", "xpack.savedObjectsTagging.validation.name.errorTooLong": "标签名称不能超过 {length} 个字符", "xpack.savedObjectsTagging.validation.name.errorTooShort": "标签名称必须至少有 {length} 个字符", "xpack.searchProfiler.advanceTimeDescription": "将迭代器推进至下一文档所用时间。", diff --git a/x-pack/test/plugin_functional/es_archives/global_search/search_syntax/data.json b/x-pack/test/plugin_functional/es_archives/global_search/search_syntax/data.json index 69220756639dc..8379290f5d9bb 100644 --- a/x-pack/test/plugin_functional/es_archives/global_search/search_syntax/data.json +++ b/x-pack/test/plugin_functional/es_archives/global_search/search_syntax/data.json @@ -88,6 +88,24 @@ } } +{ + "type": "doc", + "value": { + "id": "tag:tag-special-chars", + "index": ".kibana", + "source": { + "tag": { + "name": "my%tag", + "description": "Special chars", + "color": "#AA0077" + }, + "type": "tag", + "updated_at": "2017-09-21T18:49:16.270Z" + }, + "type": "doc" + } +} + { "type": "doc", "value": { @@ -356,3 +374,41 @@ } } } + +{ + "type": "doc", + "value": { + "id": "dashboard:ref-to-tag-special-chars", + "index": ".kibana", + "source": { + "dashboard": { + "title": "dashboard 4 (tag-special-chars)", + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"animal\",\"value\":\"dog\",\"params\":{\"query\":\"dog\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"animal\":{\"query\":\"dog\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeFrom": "Mon Apr 09 2018 17:56:08 GMT-0400", + "timeRestore": true, + "timeTo": "Wed Apr 11 2018 17:56:08 GMT-0400", + "version": 1 + }, + "migrationVersion": { + "dashboard": "7.3.0" + }, + "references": [ + { + "id": "tag-special-chars", + "name": "tag-special-ref", + "type": "tag" + } + ], + "type": "dashboard", + "updated_at": "2018-04-11T21:57:52.253Z" + } + } +} + + diff --git a/x-pack/test/plugin_functional/test_suites/global_search/global_search_bar.ts b/x-pack/test/plugin_functional/test_suites/global_search/global_search_bar.ts index f0c70ee8f718d..6f84440fc27e6 100644 --- a/x-pack/test/plugin_functional/test_suites/global_search/global_search_bar.ts +++ b/x-pack/test/plugin_functional/test_suites/global_search/global_search_bar.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { - describe('GlobalSearchBar', function () { + describe('TOTO GlobalSearchBar', function () { const { common, navigationalSearch } = getPageObjects(['common', 'navigationalSearch']); const esArchiver = getService('esArchiver'); const browser = getService('browser'); @@ -61,6 +61,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'dashboard 1 (tag-2)', 'dashboard 2 (tag-3)', 'dashboard 3 (tag-1 and tag-3)', + 'dashboard 4 (tag-special-chars)', ]); }); it('shows a suggestion when searching for a term matching a tag name', async () => { @@ -94,6 +95,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'dashboard 1 (tag-2)', 'dashboard 2 (tag-3)', 'dashboard 3 (tag-1 and tag-3)', + 'dashboard 4 (tag-special-chars)', ]); }); @@ -111,6 +113,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'dashboard 1 (tag-2)', 'dashboard 2 (tag-3)', 'dashboard 3 (tag-1 and tag-3)', + 'dashboard 4 (tag-special-chars)', ]); }); @@ -181,6 +184,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(results.map((result) => result.label)).to.eql(['My awesome vis (tag-4)']); }); + it('allows to filter by tags containing special characters', async () => { + await navigationalSearch.searchFor('tag:"my%tag"'); + + const results = await navigationalSearch.getDisplayedResults(); + + expect(results.map((result) => result.label)).to.eql(['dashboard 4 (tag-special-chars)']); + }); + it('returns no results when searching for an unknown tag', async () => { await navigationalSearch.searchFor('tag:unknown'); diff --git a/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/create.ts b/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/create.ts index bd7fa7538703c..30008e635b628 100644 --- a/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/create.ts +++ b/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/create.ts @@ -60,7 +60,7 @@ export default function ({ getService }: FtrProviderContext) { await supertest .post(`/api/saved_objects_tagging/tags/create`) .send({ - name: 'Inv%li& t@g n*me', + name: 'a', description: 'some desc', color: 'this is not a valid color', }) @@ -74,7 +74,7 @@ export default function ({ getService }: FtrProviderContext) { valid: false, warnings: [], errors: { - name: 'Tag name can only include a-z, 0-9, _, -,:.', + name: 'Tag name must be at least 2 characters', color: 'Tag color must be a valid hex color', }, }, diff --git a/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/update.ts b/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/update.ts index 7b4298607c666..ddf39ccf90b34 100644 --- a/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/update.ts +++ b/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/update.ts @@ -78,7 +78,7 @@ export default function ({ getService }: FtrProviderContext) { await supertest .post(`/api/saved_objects_tagging/tags/tag-1`) .send({ - name: 'Inv%li& t@g n*me', + name: 'a', description: 'some desc', color: 'this is not a valid color', }) @@ -92,7 +92,7 @@ export default function ({ getService }: FtrProviderContext) { valid: false, warnings: [], errors: { - name: 'Tag name can only include a-z, 0-9, _, -,:.', + name: 'Tag name must be at least 2 characters', color: 'Tag color must be a valid hex color', }, }, diff --git a/x-pack/test/saved_object_tagging/functional/tests/create.ts b/x-pack/test/saved_object_tagging/functional/tests/create.ts index b62e9a70b43e8..2f2db856c0657 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/create.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/create.ts @@ -54,7 +54,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await tagModal.openCreate(); await tagModal.fillForm( { - name: 'invalid&$%name', + name: 'a', description: 'The name will fails validation', color: '#FF00CC', }, @@ -73,7 +73,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await tagModal.openCreate(); await tagModal.fillForm( { - name: 'invalid&$%name', + name: 'a', description: 'The name will fails validation', color: '#FF00CC', }, diff --git a/x-pack/test/saved_object_tagging/functional/tests/edit.ts b/x-pack/test/saved_object_tagging/functional/tests/edit.ts index 1883d3f23dc9d..1de101433179d 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/edit.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/edit.ts @@ -71,7 +71,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await tagModal.openEdit('tag-2'); await tagModal.fillForm( { - name: 'invalid&$%name', + name: 'a', }, { submit: true } ); @@ -88,7 +88,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await tagModal.openEdit('tag-2'); await tagModal.fillForm( { - name: 'invalid&$%name', + name: 'a', description: 'edited description', color: '#FF00CC', }, From f4c43002017a93ad77c4b8c2abe4bc5ee6d1cded Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 25 Jan 2021 15:35:30 +0100 Subject: [PATCH 28/62] [Discover] Deangularize $element and $timeout (#88214) * Remove $element for document.getElementById * Remove $timeout --- .../public/application/angular/discover.js | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 75fed9f809aa6..946baa7f4ecb1 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -175,7 +175,7 @@ app.directive('discoverApp', function () { }; }); -function discoverController($element, $route, $scope, $timeout, Promise) { +function discoverController($route, $scope, Promise) { const { isDefault: isDefaultType } = indexPatternsUtils; const subscriptions = new Subscription(); const refetch$ = new Subject(); @@ -725,20 +725,20 @@ function discoverController($element, $route, $scope, $timeout, Promise) { $route.reload(); }; - $scope.onSkipBottomButtonClick = function () { + $scope.onSkipBottomButtonClick = async () => { // show all the Rows $scope.minimumVisibleRows = $scope.hits; // delay scrolling to after the rows have been rendered - const bottomMarker = $element.find('#discoverBottomMarker'); - $timeout(() => { - bottomMarker.focus(); - // The anchor tag is not technically empty (it's a hack to make Safari scroll) - // so the browser will show a highlight: remove the focus once scrolled - $timeout(() => { - bottomMarker.blur(); - }, 0); - }, 0); + const bottomMarker = document.getElementById('discoverBottomMarker'); + const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + + while ($scope.rows.length !== document.getElementsByClassName('kbnDocTable__row').length) { + await wait(50); + } + bottomMarker.focus(); + await wait(50); + bottomMarker.blur(); }; $scope.newQuery = function () { From eed938396949f9ca2127c2859eded50781b3f14b Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Mon, 25 Jan 2021 15:36:48 +0100 Subject: [PATCH 29/62] Migrations v2 docs (#88820) * Migrations v2 docs * Not all kibana distributions automatically restarted a killed process * Mention that we add a write block to the outdated index * Formating: collapse three notes into a single note with three bullet points * Update docs/setup/upgrade/upgrade-standard.asciidoc Co-authored-by: Josh Dover <1813008+joshdover@users.noreply.github.com> * Add table of outdated / upgraded indices per version of Kibana * Review feedback: separate section for multi-instance upgrade migrations * Review feedback: link to saved objects management * Review feedback: stronger wording for not deleting any indices Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Josh Dover <1813008+joshdover@users.noreply.github.com> --- .../setup/upgrade/upgrade-migrations.asciidoc | 79 +++++++++---------- docs/setup/upgrade/upgrade-standard.asciidoc | 14 ++-- 2 files changed, 46 insertions(+), 47 deletions(-) diff --git a/docs/setup/upgrade/upgrade-migrations.asciidoc b/docs/setup/upgrade/upgrade-migrations.asciidoc index 74d097164c4a7..7436536d22781 100644 --- a/docs/setup/upgrade/upgrade-migrations.asciidoc +++ b/docs/setup/upgrade/upgrade-migrations.asciidoc @@ -1,11 +1,13 @@ [[upgrade-migrations]] === Upgrade migrations -Every time {kib} is upgraded it checks to see if all saved objects, such as dashboards, visualizations, and index patterns, are compatible with the new version. If any saved objects need to be updated, then the automatic saved object migration process is kicked off. +Every time {kib} is upgraded it will perform an upgrade migration to ensure that all <> are compatible with the new version. NOTE: 6.7 includes an https://www.elastic.co/guide/en/kibana/6.7/upgrade-assistant.html[Upgrade Assistant] to help you prepare for your upgrade to 7.0. To access the assistant, go to *Management > 7.0 Upgrade Assistant*. +WARNING: {kib} 7.12.0 and later uses a new migration process and index naming scheme. Be sure to read the documentation for your version of {kib} before proceeding. + WARNING: The following instructions assumes {kib} is using the default index names. If the `kibana.index` or `xpack.tasks.index` configuration settings were changed these instructions will have to be adapted accordingly. [float] @@ -14,19 +16,35 @@ WARNING: The following instructions assumes {kib} is using the default index nam Saved objects are stored in two indices: -* `.kibana_N`, or if set, the `kibana.index` configuration setting -* `.kibana_task_manager_N`, or if set, the `xpack.tasks.index` configuration setting +* `.kibana_{kibana_version}_001`, or if the `kibana.index` configuration setting is set `.{kibana.index}_{kibana_version}_001`. E.g. for Kibana v7.12.0 `.kibana_7.12.0_001`. +* `.kibana_task_manager_{kibana_version}_001`, or if the `xpack.tasks.index` configuration setting is set `.{xpack.tasks.index}_{kibana_version}_001` E.g. for Kibana v7.12.0 `.kibana_task_manager_7.12.0_001`. -For each of these indices, `N` is a number that increments every time {kib} runs an upgrade migration on that index. The index aliases `.kibana` and `.kibana_task_manager` point to the most up-to-date index. +The index aliases `.kibana` and `.kibana_task_manager` will always point to the most up-to-date version indices. + +The first time a newer {kib} starts, it will first perform an upgrade migration before starting plugins or serving HTTP traffic. To prevent losing acknowledged writes old nodes should be shutdown before starting the upgrade. To reduce the likelihood of old nodes losing acknowledged writes, {kib} 7.12.0 and later will add a write block to the outdated index. Table 1 lists the saved objects indices used by previous versions of {kib}. + +.Saved object indices and aliases per {kib} version +[options="header"] +[cols="a,a,a"] +|======================= +|Upgrading from version | Outdated index (alias) | Upgraded index (alias) +| 6.0.0 through 6.4.x | `.kibana` 1.3+^.^| `.kibana_7.12.0_001` +(`.kibana` alias) + +`.kibana_task_manager_7.12.0_001` (`.kibana_task_manager` alias) +| 6.5.0 through 7.3.x | `.kibana_N` (`.kibana` alias) +| 7.4.0 through 7.11.x +| `.kibana_N` (`.kibana` alias) -While {kib} is starting up and before serving any HTTP traffic, it checks to see if any internal mapping changes or data transformations for existing saved objects are required. +`.kibana_task_manager_N` (`.kibana_task_manager` alias) +|======================= -When changes are necessary, a new migration is started. To ensure that only one {kib} instance performs the migration, each instance will attempt to obtain a migration lock by creating a new `.kibana_N+1` index. The instance that succeeds in creating the index will then read batches of documents from the existing index, migrate them, and write them to the new index. Once the objects are migrated, the lock is released by pointing the `.kibana` index alias the new upgraded `.kibana_N+1` index. +==== Upgrading multiple {kib} instances +When upgrading several {kib} instances connected to the same {es} cluster, ensure that all outdated instances are shutdown before starting the upgrade. -Instances that failed to acquire a lock will log `Another Kibana instance appears to be migrating the index. Waiting for that migration to complete`. The instance will then wait until `.kibana` points to an upgraded index before starting up and serving HTTP traffic. +Kibana does not support rolling upgrades. However, once outdated instances are shutdown, all upgraded instances can be started in parallel in which case all instances will participate in the upgrade migration in parallel. -NOTE: Prior to 6.5.0, saved objects were stored directly in an index named `.kibana`. After upgrading to version 6.5+, {kib} will migrate this index into `.kibana_N` and set `.kibana` up as an index alias. + -Prior to 7.4.0, task manager tasks were stored directly in an index name `.kibana_task_manager`. After upgrading to version 7.4+, {kib} will migrate this index into `.kibana_task_manager_N` and set `.kibana_task_manager` up as an index alias. +For large deployments with more than 10 {kib} instances and more than 10 000 saved objects, the upgrade downtime can be reduced by bringing up a single {kib} instance and waiting for it to complete the upgrade migration before bringing up the remaining instances. [float] [[preventing-migration-failures]] @@ -54,50 +72,31 @@ Problems with your {es} cluster can prevent {kib} upgrades from succeeding. Ensu * a "green" cluster status [float] -===== Running different versions of {kib} connected to the same {es} index -Kibana does not support rolling upgrades. Stop all {kib} instances before starting a newer version to prevent upgrade failures and data loss. +===== Different versions of {kib} connected to the same {es} index +When different versions of {kib} are attempting an upgrade migration in parallel this can lead to migration failures. Ensure that all {kib} instances are running the same version, configuration and plugins. [float] ===== Incompatible `xpack.tasks.index` configuration setting -For {kib} < 7.5.1, if the task manager index is set to `.tasks` with the configuration setting `xpack.tasks.index: ".tasks"`, upgrade migrations will fail. {kib} 7.5.1 and later prevents this by refusing to start with an incompatible configuration setting. +For {kib} versions prior to 7.5.1, if the task manager index is set to `.tasks` with the configuration setting `xpack.tasks.index: ".tasks"`, upgrade migrations will fail. {kib} 7.5.1 and later prevents this by refusing to start with an incompatible configuration setting. [float] [[resolve-migrations-failures]] ==== Resolving migration failures -If {kib} terminates unexpectedly while migrating a saved object index, manual intervention is required before {kib} will attempt to perform the migration again. Follow the advice in (preventing migration failures)[preventing-migration-failures] before retrying a migration upgrade. - -As mentioned above, {kib} will create a migration lock for each index that requires a migration by creating a new `.kibana_N+1` index. For example: if the `.kibana_task_manager` alias is pointing to `.kibana_task_manager_5` then the first {kib} that succeeds in creating `.kibana_task_manager_6` will obtain the lock to start migrations. - -However, if the instance that obtained the lock fails to migrate the index, all other {kib} instances will be blocked from performing this migration. This includes the instance that originally obtained the lock, it will be blocked from retrying the migration even when restarted. - -[float] -===== Retry a migration by restoring a backup snapshot: - -1. Before proceeding ensure that you have a recent and successful backup snapshot of all `.kibana*` indices. -2. Shutdown all {kib} instances to be 100% sure that there are no instances currently performing a migration. -3. Delete all saved object indices with `DELETE /.kibana*` -4. Restore the `.kibana* indices and their aliases from the backup snapshot. See {es} {ref}/modules-snapshots.html[snapshots] -5. Start up all {kib} instances to retry the upgrade migration. - -[float] -===== (Not recommended) Retry a migration without a backup snapshot: +If {kib} terminates unexpectedly while migrating a saved object index it will automatically attempt to perform the migration again once the process has restarted. Do not delete any saved objects indices to attempt to fix a failed migration. Unlike previous versions, {kib} version 7.12.0 and later does not require deleting any indices to release a failed migration lock. -1. Shutdown all {kib} instances to be 100% sure that there are no instances currently performing a migration. -2. Identify any migration locks by comparing the output of `GET /_cat/aliases` and `GET /_cat/indices`. If e.g. `.kibana` is pointing to `.kibana_4` and there is a `.kibana_5` index, the `.kibana_5` index will act like a migration lock blocking further attempts. Be sure to check both the `.kibana` and `.kibana_task_manager` aliases and their indices. -3. Remove any migration locks e.g. `DELETE /.kibana_5`. -4. Start up all {kib} instances. +If upgrade migrations fail repeatedly, follow the advice in (preventing migration failures)[preventing-migration-failures]. Once the root cause for the migration failure has been addressed, {kib} will automatically retry the migration without any further intervention. If you're unable to resolve a failed migration following these steps, please contact support. [float] [[upgrade-migrations-rolling-back]] ==== Rolling back to a previous version of {kib} -If you've followed the advice in (preventing migration failures)[preventing-migration-failures] and (resolving migration failures)[resolve-migrations-failures] and {kib} is still not able to upgrade successfully, you might choose to rollback {kib} until you're able to identify the root cause. +If you've followed the advice in (preventing migration failures)[preventing-migration-failures] and (resolving migration failures)[resolve-migrations-failures] and {kib} is still not able to upgrade successfully, you might choose to rollback {kib} until you're able to identify and fix the root cause. WARNING: Before rolling back {kib}, ensure that the version you wish to rollback to is compatible with your {es} cluster. If the version you're rolling back to is not compatible, you will have to also rollback {es}. + Any changes made after an upgrade will be lost when rolling back to a previous version. -In order to rollback after a failed upgrade migration, the saved object indices might also have to be rolled back to be compatible with the previous {kibana} version. +In order to rollback after a failed upgrade migration, the saved object indices have to be rolled back to be compatible with the previous {kibana} version. [float] ===== Rollback by restoring a backup snapshot: @@ -111,17 +110,15 @@ In order to rollback after a failed upgrade migration, the saved object indices [float] ===== (Not recommended) Rollback without a backup snapshot: -WARNING: {kib} does not run a migration for every saved object index on every upgrade. A {kib} version upgrade can cause no migrations, migrate only the `.kibana` or the `.kibana_task_manager` index or both. Carefully read the logs to ensure that you're only deleting indices created by a later version of {kib} to avoid data loss. - 1. Shutdown all {kib} instances to be 100% sure that there are no {kib} instances currently performing a migration. 2. Create a backup snapshot of the `.kibana*` indices. -3. Use the logs from the upgraded instances to identify which indices {kib} attempted to upgrade. The server logs will contain an entry like `[savedobjects-service] Creating index .kibana_4.` and/or `[savedobjects-service] Creating index .kibana_task_manager_2.` If no indices were created after upgrading {kib} then no further action is required to perform a rollback, skip ahead to step (5). If you're running multiple {kib} instances, be sure to inspect all instances' logs. -4. Delete each of the indices identified in step (2). e.g. `DELETE /.kibana_task_manager_2` -5. Inspect the output of `GET /_cat/aliases`. If either the `.kibana` and/or `.kibana_task_manager` alias is missing, these will have to be created manually. Find the latest index from the output of `GET /_cat/indices` and create the missing alias to point to the latest index. E.g. if the `.kibana` alias was missing and the latest index is `.kibana_3` create a new alias with `POST /.kibana_3/_aliases/.kibana`. +3. Delete the version specific indices created by the failed upgrade migration. E.g. if you wish to rollback from a failed upgrade to v7.12.0 `DELETE /.kibana_7.12.0_*,.kibana_task_manager_7.12.0_*` +4. Inspect the output of `GET /_cat/aliases`. If either the `.kibana` and/or `.kibana_task_manager` alias is missing, these will have to be created manually. Find the latest index from the output of `GET /_cat/indices` and create the missing alias to point to the latest index. E.g. if the `.kibana` alias was missing and the latest index is `.kibana_3` create a new alias with `POST /.kibana_3/_aliases/.kibana`. +5. Remove the write block from the rollback indices. `PUT /.kibana,.kibana_task_manager/_settings {"index.blocks.write": false}` 6. Start up {kib} on the older version you wish to rollback to. [float] [[upgrade-migrations-old-indices]] ==== Handling old `.kibana_N` indices -After migrations have completed, there will be multiple {kib} indices in {es}: (`.kibana_1`, `.kibana_2`, etc). {kib} only uses the index that the `.kibana` alias points to. The other {kib} indices can be safely deleted, but are left around as a matter of historical record, and to facilitate rolling {kib} back to a previous version. \ No newline at end of file +After migrations have completed, there will be multiple {kib} indices in {es}: (`.kibana_1`, `.kibana_2`, `.kibana_7.12.0` etc). {kib} only uses the index that the `.kibana` and `.kibana_task_manager` alias points to. The other {kib} indices can be safely deleted, but are left around as a matter of historical record, and to facilitate rolling {kib} back to a previous version. \ No newline at end of file diff --git a/docs/setup/upgrade/upgrade-standard.asciidoc b/docs/setup/upgrade/upgrade-standard.asciidoc index b27bb8867e624..b43da6aef9765 100644 --- a/docs/setup/upgrade/upgrade-standard.asciidoc +++ b/docs/setup/upgrade/upgrade-standard.asciidoc @@ -15,17 +15,17 @@ necessary remediation steps as per those instructions. [float] ==== Upgrading multiple {kib} instances -WARNING: Kibana does not support rolling upgrades. If you're running multiple {kib} instances, all instances should be stopped before upgrading. +NOTE: Kibana does not support rolling upgrades. If you're running multiple {kib} instances, all instances should be stopped before upgrading. -Different versions of {kib} running against the same {es} index, such as during a rolling upgrade, can cause upgrade migration failures and data loss. This is because acknowledged writes from the older instances could be written into the _old_ index while the migration is in progress. To prevent this from happening ensure that all old {kib} instances are shutdown before starting up instances on a newer version. - -The first instance that triggers saved object migrations will run the entire process. Any other instances started up while a migration is running will log a message and then wait until saved object migrations has completed before they start serving HTTP traffic. +Different versions of {kib} running against the same {es} index, such as during a rolling upgrade, can cause data loss. This is because older instances will continue to write saved objects in a different format than the newer instances. To prevent this from happening ensure that all old {kib} instances are shutdown before starting up instances on a newer version. [float] ==== Upgrade using a `deb` or `rpm` package . Stop the existing {kib} process using the appropriate command for your - system. If you have multiple {kib} instances connecting to the same {es} cluster ensure that all instances are stopped before proceeding to the next step to avoid data loss. + system. If you have multiple {kib} instances connecting to the same {es} + cluster ensure that all instances are stopped before proceeding to the next + step to avoid data loss. . Use `rpm` or `dpkg` to install the new package. All files should be placed in their proper locations and config files should not be overwritten. + @@ -65,5 +65,7 @@ and becomes a new instance in the monitoring data. . Install the appropriate versions of all your plugins for your new installation using the `kibana-plugin` script. Check out the <> documentation for more information. -. Stop the old {kib} process. If you have multiple {kib} instances connecting to the same {es} cluster ensure that all instances are stopped before proceeding to the next step to avoid data loss. +. Stop the old {kib} process. If you have multiple {kib} instances connecting + to the same {es} cluster ensure that all instances are stopped before + proceeding to the next step to avoid data loss. . Start the new {kib} process. From 164d6b2d9934927373b1ad01246613674c05accf Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 25 Jan 2021 15:51:11 +0100 Subject: [PATCH 30/62] [discover] add valye formatter on y axis and display only integer values(#88941) --- .../public/application/angular/directives/histogram.tsx | 6 ++++++ .../public/application/angular/helpers/point_series.ts | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/plugins/discover/public/application/angular/directives/histogram.tsx b/src/plugins/discover/public/application/angular/directives/histogram.tsx index ff10feea46d47..b12de3f4496c5 100644 --- a/src/plugins/discover/public/application/angular/directives/histogram.tsx +++ b/src/plugins/discover/public/application/angular/directives/histogram.tsx @@ -154,6 +154,10 @@ export class DiscoverHistogram extends Component xAxisFormatter.convert(value)} /> ; xAxisOrderedValues: number[]; xAxisFormat: Dimension['format']; + yAxisFormat: Dimension['format']; xAxisLabel: Column['name']; yAxisLabel?: Column['name']; ordered: Ordered; @@ -76,7 +77,7 @@ export const buildPointSeriesData = (table: Table, dimensions: Dimensions) => { chart.xAxisOrderedValues = uniq(table.rows.map((r) => r[xAccessor] as number)); chart.xAxisFormat = x.format; chart.xAxisLabel = table.columns[x.accessor].name; - + chart.yAxisFormat = y.format; const { intervalESUnit, intervalESValue, interval, bounds } = x.params; chart.ordered = { date: true, From e7e42a47117da9ad2415f0108736842dd3798a8c Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 25 Jan 2021 16:07:53 +0100 Subject: [PATCH 31/62] [Uptime] Display networks requests total (#88672) --- .../common/runtime_types/network_events.ts | 1 + .../waterfall/waterfall_chart_container.tsx | 5 +- .../waterfall/waterfall_chart_wrapper.tsx | 5 +- .../network_requests_total.test.tsx | 28 ++++++++++ .../components/network_requests_total.tsx | 44 ++++++++++++++++ .../synthetics/waterfall/components/styles.ts | 7 ++- .../waterfall/components/waterfall_chart.tsx | 16 +++++- .../waterfall/context/waterfall_chart.tsx | 17 +++++- .../public/state/reducers/network_events.ts | 12 ++++- .../lib/requests/get_network_events.test.ts | 52 +++++++++++-------- .../server/lib/requests/get_network_events.ts | 45 +++++++++------- .../network_events/get_network_events.ts | 4 +- 12 files changed, 183 insertions(+), 53 deletions(-) create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/network_requests_total.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/network_requests_total.tsx diff --git a/x-pack/plugins/uptime/common/runtime_types/network_events.ts b/x-pack/plugins/uptime/common/runtime_types/network_events.ts index 6104758f28fd8..fc666c803e2c3 100644 --- a/x-pack/plugins/uptime/common/runtime_types/network_events.ts +++ b/x-pack/plugins/uptime/common/runtime_types/network_events.ts @@ -41,6 +41,7 @@ export type NetworkEvent = t.TypeOf; export const SyntheticsNetworkEventsApiResponseType = t.type({ events: t.array(NetworkEventType), + total: t.number, }); export type SyntheticsNetworkEventsApiResponse = t.TypeOf< diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx index 7657ca7f9c64a..680e3f257841e 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx @@ -59,7 +59,10 @@ export const WaterfallChartContainer: React.FC = ({ checkGroup, stepIndex )} {networkEvents && !networkEvents.loading && networkEvents.events.length > 0 && ( - + )} ); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx index a84765c4ea154..7b904511b58ab 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx @@ -48,10 +48,11 @@ export const renderLegendItem: RenderItem = (item) => { }; interface Props { + total: number; data: NetworkItems; } -export const WaterfallChartWrapper: React.FC = ({ data }) => { +export const WaterfallChartWrapper: React.FC = ({ data, total }) => { const [networkData] = useState(data); const { series, domain } = useMemo(() => { @@ -66,6 +67,8 @@ export const WaterfallChartWrapper: React.FC = ({ data }) => { return ( { + it('message in case total is greater than fetched', () => { + const { getByText, getByLabelText } = render( + + ); + + expect(getByText('First 1000/1100 network requests')).toBeInTheDocument(); + expect(getByLabelText('Info')).toBeInTheDocument(); + }); + + it('message in case total is equal to fetched requests', () => { + const { getByText } = render( + + ); + + expect(getByText('500 network requests')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/network_requests_total.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/network_requests_total.tsx new file mode 100644 index 0000000000000..c54e32238f81c --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/network_requests_total.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiIconTip } from '@elastic/eui'; +import { NetworkRequestsTotalStyle } from './styles'; + +interface Props { + totalNetworkRequests: number; + fetchedNetworkRequests: number; +} + +export const NetworkRequestsTotal = ({ totalNetworkRequests, fetchedNetworkRequests }: Props) => { + return ( + + + {i18n.translate('xpack.uptime.synthetics.waterfall.requestsTotalMessage', { + defaultMessage: '{numNetworkRequests} network requests', + values: { + numNetworkRequests: + totalNetworkRequests > fetchedNetworkRequests + ? i18n.translate('xpack.uptime.synthetics.waterfall.requestsTotalMessage.first', { + defaultMessage: 'First {count}', + values: { count: `${fetchedNetworkRequests}/${totalNetworkRequests}` }, + }) + : totalNetworkRequests, + }, + })} + + {totalNetworkRequests > fetchedNetworkRequests && ( + + )} + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts index 1f70354db154e..7bf5100730f5e 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiPanel, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { rgba } from 'polished'; import { euiStyled } from '../../../../../../../observability/public'; import { FIXED_AXIS_HEIGHT } from './constants'; @@ -103,3 +103,8 @@ export const WaterfallChartTooltip = euiStyled.div` color: ${(props) => props.theme.eui.euiColorLightestShade}; padding: ${(props) => props.theme.eui.paddingSizes.s}; `; + +export const NetworkRequestsTotalStyle = euiStyled(EuiText)` + line-height: ${FIXED_AXIS_HEIGHT}px; + margin-left: ${(props) => props.theme.eui.paddingSizes.m} +`; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx index e937c3d35ec08..e449fed6decf4 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx @@ -37,6 +37,7 @@ import { BAR_HEIGHT, CANVAS_MAX_ITEMS, MAIN_GROW_SIZE, SIDEBAR_GROW_SIZE } from import { Sidebar } from './sidebar'; import { Legend } from './legend'; import { useBarCharts } from './use_bar_charts'; +import { NetworkRequestsTotal } from './network_requests_total'; const Tooltip = (tooltipInfo: TooltipInfo) => { const { data, renderTooltipItem } = useWaterfallContext(); @@ -84,7 +85,13 @@ export const WaterfallChart = ({ maxHeight = '800px', fullHeight = false, }: WaterfallChartProps) => { - const { data, sidebarItems, legendItems } = useWaterfallContext(); + const { + data, + sidebarItems, + legendItems, + totalNetworkRequests, + fetchedNetworkRequests, + } = useWaterfallContext(); const [darkMode] = useUiSetting$('theme:darkMode'); @@ -115,7 +122,12 @@ export const WaterfallChart = ({ {shouldRenderSidebar && ( - + + + )} diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx index ccee9d7994c80..4cf22f317bbd4 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx @@ -8,6 +8,8 @@ import React, { createContext, useContext, Context } from 'react'; import { WaterfallData, WaterfallDataEntry } from '../types'; export interface IWaterfallContext { + totalNetworkRequests: number; + fetchedNetworkRequests: number; data: WaterfallData; sidebarItems?: unknown[]; legendItems?: unknown[]; @@ -20,6 +22,8 @@ export interface IWaterfallContext { export const WaterfallContext = createContext>({}); interface ProviderProps { + totalNetworkRequests: number; + fetchedNetworkRequests: number; data: IWaterfallContext['data']; sidebarItems?: IWaterfallContext['sidebarItems']; legendItems?: IWaterfallContext['legendItems']; @@ -32,9 +36,20 @@ export const WaterfallProvider: React.FC = ({ sidebarItems, legendItems, renderTooltipItem, + totalNetworkRequests, + fetchedNetworkRequests, }) => { return ( - + {children} ); diff --git a/x-pack/plugins/uptime/public/state/reducers/network_events.ts b/x-pack/plugins/uptime/public/state/reducers/network_events.ts index 44a23b0fa53d7..666617f785182 100644 --- a/x-pack/plugins/uptime/public/state/reducers/network_events.ts +++ b/x-pack/plugins/uptime/public/state/reducers/network_events.ts @@ -18,6 +18,7 @@ export interface NetworkEventsState { [checkGroup: string]: { [stepIndex: number]: { events: NetworkEvent[]; + total: number; loading: boolean; error?: Error; }; @@ -45,16 +46,19 @@ export const networkEventsReducer = handleActions( ...state[checkGroup][stepIndex], loading: true, events: [], + total: 0, } : { loading: true, events: [], + total: 0, }, } : { [stepIndex]: { loading: true, events: [], + total: 0, }, }, }), @@ -62,7 +66,7 @@ export const networkEventsReducer = handleActions( [String(getNetworkEventsSuccess)]: ( state: NetworkEventsState, { - payload: { events, checkGroup, stepIndex }, + payload: { events, total, checkGroup, stepIndex }, }: Action ) => { return { @@ -74,16 +78,19 @@ export const networkEventsReducer = handleActions( ...state[checkGroup][stepIndex], loading: false, events, + total, } : { loading: false, events, + total, }, } : { [stepIndex]: { loading: false, events, + total, }, }, }; @@ -101,11 +108,13 @@ export const networkEventsReducer = handleActions( ...state[checkGroup][stepIndex], loading: false, events: [], + total: 0, error, } : { loading: false, events: [], + total: 0, error, }, } @@ -113,6 +122,7 @@ export const networkEventsReducer = handleActions( [stepIndex]: { loading: false, events: [], + total: 0, error, }, }, diff --git a/x-pack/plugins/uptime/server/lib/requests/get_network_events.test.ts b/x-pack/plugins/uptime/server/lib/requests/get_network_events.test.ts index e8618fabc4cca..2d590e80ca42d 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_network_events.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_network_events.test.ts @@ -158,6 +158,7 @@ describe('getNetworkEvents', () => { esClient.search.mockResolvedValueOnce({ body: { hits: { + total: { value: 1 }, hits: mockHits, }, }, @@ -196,6 +197,7 @@ describe('getNetworkEvents', () => { }, }, "size": 1000, + "track_total_hits": true, }, "index": "heartbeat-8*", }, @@ -210,6 +212,7 @@ describe('getNetworkEvents', () => { esClient.search.mockResolvedValueOnce({ body: { hits: { + total: { value: 1 }, hits: mockHits, }, }, @@ -222,30 +225,33 @@ describe('getNetworkEvents', () => { }); expect(result).toMatchInlineSnapshot(` - Array [ - Object { - "loadEndTime": 3287298.251, - "method": "GET", - "mimeType": "image/gif", - "requestSentTime": 3287154.973, - "requestStartTime": 3287155.502, - "status": 200, - "timestamp": "2020-12-14T10:46:39.183Z", - "timings": Object { - "blocked": 0.21400000014182297, - "connect": -1, - "dns": -1, - "proxy": -1, - "queueing": 0.5289999999149586, - "receive": 0.5340000002433953, - "send": 0.18799999998009298, - "ssl": -1, - "total": 143.27800000000934, - "wait": 141.81299999972907, + Object { + "events": Array [ + Object { + "loadEndTime": 3287298.251, + "method": "GET", + "mimeType": "image/gif", + "requestSentTime": 3287154.973, + "requestStartTime": 3287155.502, + "status": 200, + "timestamp": "2020-12-14T10:46:39.183Z", + "timings": Object { + "blocked": 0.21400000014182297, + "connect": -1, + "dns": -1, + "proxy": -1, + "queueing": 0.5289999999149586, + "receive": 0.5340000002433953, + "send": 0.18799999998009298, + "ssl": -1, + "total": 143.27800000000934, + "wait": 141.81299999972907, + }, + "url": "www.test.com", }, - "url": "www.test.com", - }, - ] + ], + "total": 1, + } `); }); }); diff --git a/x-pack/plugins/uptime/server/lib/requests/get_network_events.ts b/x-pack/plugins/uptime/server/lib/requests/get_network_events.ts index 1353175a8f94d..ec1fffd62350d 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_network_events.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_network_events.ts @@ -14,9 +14,10 @@ interface GetNetworkEventsParams { export const getNetworkEvents: UMElasticsearchQueryFn< GetNetworkEventsParams, - NetworkEvent[] + { events: NetworkEvent[]; total: number } > = async ({ uptimeEsClient, checkGroup, stepIndex }) => { const params = { + track_total_hits: true, query: { bool: { filter: [ @@ -36,24 +37,28 @@ export const getNetworkEvents: UMElasticsearchQueryFn< const microToMillis = (micro: number): number => (micro === -1 ? -1 : micro * 1000); - return result.hits.hits.map((event: any) => { - const requestSentTime = microToMillis(event._source.synthetics.payload.request_sent_time); - const loadEndTime = microToMillis(event._source.synthetics.payload.load_end_time); - const requestStartTime = - event._source.synthetics.payload.response && event._source.synthetics.payload.response.timing - ? microToMillis(event._source.synthetics.payload.response.timing.request_time) - : undefined; + return { + total: result.hits.total.value, + events: result.hits.hits.map((event: any) => { + const requestSentTime = microToMillis(event._source.synthetics.payload.request_sent_time); + const loadEndTime = microToMillis(event._source.synthetics.payload.load_end_time); + const requestStartTime = + event._source.synthetics.payload.response && + event._source.synthetics.payload.response.timing + ? microToMillis(event._source.synthetics.payload.response.timing.request_time) + : undefined; - return { - timestamp: event._source['@timestamp'], - method: event._source.synthetics.payload?.method, - url: event._source.synthetics.payload?.url, - status: event._source.synthetics.payload?.status, - mimeType: event._source.synthetics.payload?.response?.mime_type, - requestSentTime, - requestStartTime, - loadEndTime, - timings: event._source.synthetics.payload.timings, - }; - }); + return { + timestamp: event._source['@timestamp'], + method: event._source.synthetics.payload?.method, + url: event._source.synthetics.payload?.url, + status: event._source.synthetics.payload?.status, + mimeType: event._source.synthetics.payload?.response?.mime_type, + requestSentTime, + requestStartTime, + loadEndTime, + timings: event._source.synthetics.payload.timings, + }; + }), + }; }; diff --git a/x-pack/plugins/uptime/server/rest_api/network_events/get_network_events.ts b/x-pack/plugins/uptime/server/rest_api/network_events/get_network_events.ts index f24b319baff00..7a6355ea4247d 100644 --- a/x-pack/plugins/uptime/server/rest_api/network_events/get_network_events.ts +++ b/x-pack/plugins/uptime/server/rest_api/network_events/get_network_events.ts @@ -26,8 +26,6 @@ export const createNetworkEventsRoute: UMRestApiRouteFactory = (libs: UMServerLi stepIndex, }); - return { - events: result, - }; + return result; }, }); From 1714b22de72bd63000649516b4c0cd4068ea00f3 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 25 Jan 2021 17:41:25 +0200 Subject: [PATCH 32/62] [Security Solution][Case] Improve cases and actions docs (#87817) --- x-pack/plugins/actions/README.md | 179 +++++++++++++++++++------------ x-pack/plugins/case/README.md | 40 ++++--- 2 files changed, 135 insertions(+), 84 deletions(-) diff --git a/x-pack/plugins/actions/README.md b/x-pack/plugins/actions/README.md index 12c3ab12a6998..9472cbf400a6a 100644 --- a/x-pack/plugins/actions/README.md +++ b/x-pack/plugins/actions/README.md @@ -69,21 +69,26 @@ Table of Contents - [`secrets`](#secrets-6) - [`params`](#params-6) - [`subActionParams (pushToService)`](#subactionparams-pushtoservice) - - [`subActionParams (getFields)`](#subactionparams-getfields-1) + - [`subActionParams (getFields)`](#subactionparams-getfields) - [Jira](#jira) - [`config`](#config-7) - [`secrets`](#secrets-7) - [`params`](#params-7) - [`subActionParams (pushToService)`](#subactionparams-pushtoservice-1) + - [`subActionParams (getIncident)`](#subactionparams-getincident) - [`subActionParams (issueTypes)`](#subactionparams-issuetypes) - - [`subActionParams (getFields)`](#subactionparams-getfields-2) - - [`subActionParams (pushToService)`](#subactionparams-pushtoservice-2) + - [`subActionParams (fieldsByIssueType)`](#subactionparams-fieldsbyissuetype) + - [`subActionParams (issues)`](#subactionparams-issues) + - [`subActionParams (issue)`](#subactionparams-issue) + - [`subActionParams (getFields)`](#subactionparams-getfields-1) - [IBM Resilient](#ibm-resilient) - [`config`](#config-8) - [`secrets`](#secrets-8) - [`params`](#params-8) - - [`subActionParams (pushToService)`](#subactionparams-pushtoservice-3) - - [`subActionParams (getFields)`](#subactionparams-getfields-3) + - [`subActionParams (pushToService)`](#subactionparams-pushtoservice-2) + - [`subActionParams (getFields)`](#subactionparams-getfields-2) + - [`subActionParams (incidentTypes)`](#subactionparams-incidenttypes) + - [`subActionParams (severity)`](#subactionparams-severity) - [Command Line Utility](#command-line-utility) - [Developing New Action Types](#developing-new-action-types) - [licensing](#licensing) @@ -526,17 +531,17 @@ The PagerDuty action uses the [V2 Events API](https://v2.developer.pagerduty.com ### `params` -| Property | Description | Type | -| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | -| eventAction | One of `trigger` _(default)_, `resolve`, or `acknowlege`. See [event action](https://v2.developer.pagerduty.com/docs/events-api-v2#event-action) for more details. | string _(optional)_ | -| dedupKey | All actions sharing this key will be associated with the same PagerDuty alert. Used to correlate trigger and resolution. The maximum length is **255** characters. See [alert deduplication](https://v2.developer.pagerduty.com/docs/events-api-v2#alert-de-duplication) for details. | string _(optional)_ | -| summary | A text summary of the event, defaults to `No summary provided`. The maximum length is **1024** characters. | string _(optional)_ | -| source | The affected system, preferably a hostname or fully qualified domain name. Defaults to `Kibana Action `. | string _(optional)_ | -| severity | The perceived severity of on the affected system. This can be one of `critical`, `error`, `warning` or `info`_(default)_. | string _(optional)_ | -| timestamp | An [ISO-8601 format date-time](https://v2.developer.pagerduty.com/v2/docs/types#datetime), indicating the time the event was detected or generated. | string _(optional)_ | -| component | The component of the source machine that is responsible for the event, for example `mysql` or `eth0`. | string _(optional)_ | -| group | Logical grouping of components of a service, for example `app-stack`. | string _(optional)_ | -| class | The class/type of the event, for example `ping failure` or `cpu load`. | string _(optional)_ | +| Property | Description | Type | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | +| eventAction | One of `trigger` _(default)_, `resolve`, or `acknowlege`. See [event action](https://v2.developer.pagerduty.com/docs/events-api-v2#event-action) for more details. | string _(optional)_ | +| dedupKey | All actions sharing this key will be associated with the same PagerDuty alert. Used to correlate trigger and resolution. The maximum length is **255** characters. See [alert deduplication](https://v2.developer.pagerduty.com/docs/events-api-v2#alert-de-duplication) for details. | string _(optional)_ | +| summary | A text summary of the event, defaults to `No summary provided`. The maximum length is **1024** characters. | string _(optional)_ | +| source | The affected system, preferably a hostname or fully qualified domain name. Defaults to `Kibana Action `. | string _(optional)_ | +| severity | The perceived severity of on the affected system. This can be one of `critical`, `error`, `warning` or `info`_(default)_. | string _(optional)_ | +| timestamp | An [ISO-8601 format date-time](https://v2.developer.pagerduty.com/v2/docs/types#datetime), indicating the time the event was detected or generated. | string _(optional)_ | +| component | The component of the source machine that is responsible for the event, for example `mysql` or `eth0`. | string _(optional)_ | +| group | Logical grouping of components of a service, for example `app-stack`. | string _(optional)_ | +| class | The class/type of the event, for example `ping failure` or `cpu load`. | string _(optional)_ | For more details see [PagerDuty v2 event parameters](https://v2.developer.pagerduty.com/v2/docs/send-an-event-events-api-v2). @@ -550,9 +555,9 @@ The ServiceNow action uses the [V2 Table API](https://developer.servicenow.com/a ### `config` -| Property | Description | Type | -| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------- | -| apiUrl | ServiceNow instance URL. | string | +| Property | Description | Type | +| -------- | ------------------------ | ------ | +| apiUrl | ServiceNow instance URL. | string | ### `secrets` @@ -563,24 +568,28 @@ The ServiceNow action uses the [V2 Table API](https://developer.servicenow.com/a ### `params` -| Property | Description | Type | -| --------------- | ------------------------------------------------------------------------------------ | ------ | -| subAction | The sub action to perform. It can be `getFields`, `pushToService`, `handshake`, and `getIncident` | string | -| subActionParams | The parameters of the sub action | object | +| Property | Description | Type | +| --------------- | --------------------------------------------------------------------- | ------ | +| subAction | The sub action to perform. It can be `getFields`, and `pushToService` | string | +| subActionParams | The parameters of the sub action | object | #### `subActionParams (pushToService)` -| Property | Description | Type | -| ------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------- | -| savedObjectId | The id of the saved object. | string | -| title | The title of the incident. | string _(optional)_ | -| description | The description of the incident. | string _(optional)_ | -| comment | A comment. | string _(optional)_ | -| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ | -| externalId | The id of the incident in ServiceNow. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ | -| severity | The name of the severity in ServiceNow. | string _(optional)_ | -| urgency | The name of the urgency in ServiceNow. | string _(optional)_ | -| impact | The name of the impact in ServiceNow. | string _(optional)_ | +| Property | Description | Type | +| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- | +| incident | The ServiceNow incident. | object | +| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ | + +The following table describes the properties of the `incident` object. + +| Property | Description | Type | +| ----------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------- | +| short_description | The title of the incident. | string | +| description | The description of the incident. | string _(optional)_ | +| externalId | The id of the incident in ServiceNow. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ | +| severity | The name of the severity in ServiceNow. | string _(optional)_ | +| urgency | The name of the urgency in ServiceNow. | string _(optional)_ | +| impact | The name of the impact in ServiceNow. | string _(optional)_ | #### `subActionParams (getFields)` @@ -596,9 +605,9 @@ The Jira action uses the [V2 API](https://developer.atlassian.com/cloud/jira/pla ### `config` -| Property | Description | Type | -| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | -| apiUrl | Jira instance URL. | string | +| Property | Description | Type | +| -------- | ------------------ | ------ | +| apiUrl | Jira instance URL. | string | ### `secrets` @@ -609,48 +618,71 @@ The Jira action uses the [V2 API](https://developer.atlassian.com/cloud/jira/pla ### `params` -| Property | Description | Type | -| --------------- | ----------------------------------------------------------------------------------------------------------------------- | ------ | -| subAction | The sub action to perform. It can be `getFields`, `pushToService`, `handshake`, `getIncident`, `issueTypes`, and `fieldsByIssueType` | string | -| subActionParams | The parameters of the sub action | object | +| Property | Description | Type | +| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------ | +| subAction | The sub action to perform. It can be `pushToService`, `getIncident`, `issueTypes`, `fieldsByIssueType`, `issues`, `issue`, and `getFields` | string | +| subActionParams | The parameters of the sub action | object | #### `subActionParams (pushToService)` -| Property | Description | Type | -| ------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------- | -| savedObjectId | The id of the saved object | string | -| title | The title of the issue | string _(optional)_ | -| description | The description of the issue | string _(optional)_ | -| externalId | The id of the issue in Jira. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ | -| issueType | The id of the issue type in Jira. | string _(optional)_ | -| priority | The name of the priority in Jira. Example: `Medium`. | string _(optional)_ | -| labels | An array of labels. | string[] _(optional)_ | -| parent | The parent issue id or key. Only for `Sub-task` issue types. | string _(optional)_ | -| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }` | object[] _(optional)_ | +| Property | Description | Type | +| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- | +| incident | The Jira incident. | object | +| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ | -#### `subActionParams (issueTypes)` +The following table describes the properties of the `incident` object. -No parameters for `issueTypes` sub-action. Provide an empty object `{}`. +| Property | Description | Type | +| ----------- | ---------------------------------------------------------------------------------------------------------------- | --------------------- | +| summary | The title of the issue | string | +| description | The description of the issue | string _(optional)_ | +| externalId | The id of the issue in Jira. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ | +| issueType | The id of the issue type in Jira. | string _(optional)_ | +| priority | The name of the priority in Jira. Example: `Medium`. | string _(optional)_ | +| labels | An array of labels. | string[] _(optional)_ | +| parent | The parent issue id or key. Only for `Sub-task` issue types. | string _(optional)_ | -#### `subActionParams (getFields)` +#### `subActionParams (getIncident)` -No parameters for `getFields` sub-action. Provide an empty object `{}`. +| Property | Description | Type | +| ---------- | --------------------------- | ------ | +| externalId | The id of the issue in Jira | string | -#### `subActionParams (pushToService)` +#### `subActionParams (issueTypes)` + +No parameters for `issueTypes` sub-action. Provide an empty object `{}`. + +#### `subActionParams (fieldsByIssueType)` | Property | Description | Type | | -------- | -------------------------------- | ------ | | id | The id of the issue type in Jira | string | +#### `subActionParams (issues)` + +| Property | Description | Type | +| -------- | ----------------------- | ------ | +| title | The title to search for | string | + +#### `subActionParams (issue)` + +| Property | Description | Type | +| -------- | --------------------------- | ------ | +| id | The id of the issue in Jira | string | + +#### `subActionParams (getFields)` + +No parameters for `getFields` sub-action. Provide an empty object `{}`. + ## IBM Resilient ID: `.resilient` ### `config` -| Property | Description | Type | -| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | -| apiUrl | IBM Resilient instance URL. | string | +| Property | Description | Type | +| -------- | --------------------------- | ------ | +| apiUrl | IBM Resilient instance URL. | string | ### `secrets` @@ -661,19 +693,24 @@ ID: `.resilient` ### `params` -| Property | Description | Type | -| --------------- | ------------------------------------------------------------------------------------ | ------ | -| subAction | The sub action to perform. It can be `getFields`, `pushToService`, `handshake`, and `getIncident` | string | -| subActionParams | The parameters of the sub action | object | +| Property | Description | Type | +| --------------- | -------------------------------------------------------------------------------------------------- | ------ | +| subAction | The sub action to perform. It can be `pushToService`, `getFields`, `incidentTypes`, and `severity` | string | +| subActionParams | The parameters of the sub action | object | #### `subActionParams (pushToService)` +| Property | Description | Type | +| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- | +| incident | The IBM Resilient incident. | object | +| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ | + +The following table describes the properties of the `incident` object. + | Property | Description | Type | | ------------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------- | -| savedObjectId | The id of the saved object | string | -| title | The title of the incident | string _(optional)_ | +| name | The title of the incident | string _(optional)_ | | description | The description of the incident | string _(optional)_ | -| comments | The comments of the incident. A comment is of the form `{ commentId: string, version: string, comment: string }` | object[] _(optional)_ | | externalId | The id of the incident in IBM Resilient. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ | | incidentTypes | An array with the ids of IBM Resilient incident types. | number[] _(optional)_ | | severityCode | IBM Resilient id of the severity code. | number _(optional)_ | @@ -682,6 +719,14 @@ ID: `.resilient` No parameters for `getFields` sub-action. Provide an empty object `{}`. +#### `subActionParams (incidentTypes)` + +No parameters for `incidentTypes` sub-action. Provide an empty object `{}`. + +#### `subActionParams (severity)` + +No parameters for `severity` sub-action. Provide an empty object `{}`. + # Command Line Utility The [`kbn-action`](https://github.com/pmuellr/kbn-action) tool can be used to send HTTP requests to the Actions plugin. For instance, to create a Slack action from the `.slack` Action Type, use the following command: diff --git a/x-pack/plugins/case/README.md b/x-pack/plugins/case/README.md index 30011148cd1e7..069441ab640ee 100644 --- a/x-pack/plugins/case/README.md +++ b/x-pack/plugins/case/README.md @@ -4,8 +4,7 @@ Elastic is developing a Case Management Workflow. Follow our progress: -- [Case API Documentation](https://documenter.getpostman.com/view/172706/SW7c2SuF?version=latest) -- [Github Meta](https://github.com/elastic/kibana/issues/50103) +- [Case API Documentation](https://www.elastic.co/guide/en/security/master/cases-overview.html) # Action types @@ -42,27 +41,28 @@ This action type has no `secrets` properties. | description | The case’s description. | string | | tags | String array containing words and phrases that help categorize cases. | string[] | | connector | Object containing the connector’s configuration. | [connector](#connector) | +| settings | Object containing the case’s settings. | [settings](#settings) | #### `subActionParams (update)` -| Property | Description | Type | -| ----------- | ---------------------------------------------------------- | ----------------------- | -| id | The ID of the case being updated. | string | -| tile | The updated case title. | string | -| description | The updated case description. | string | -| tags | The updated case tags. | string | -| connector | Object containing the connector’s configuration. | [connector](#connector) | -| status | The updated case status, which can be: `open` or `closed`. | string | -| version | The current case version. | string | +| Property | Description | Type | +| ----------- | ------------------------------------------------------------------------- | ----------------------- | +| id | The ID of the case being updated. | string | +| tile | The updated case title. | string | +| description | The updated case description. | string | +| tags | The updated case tags. | string | +| connector | Object containing the connector’s configuration. | [connector](#connector) | +| status | The updated case status, which can be: `open`, `in-progress` or `closed`. | string | +| settings | Object containing the case’s settings. | [settings](#settings) | +| version | The current case version. | string | #### `subActionParams (addComment)` -| Property | Description | Type | -| -------- | ----------------------------------------------------------------------- | ----------------- | -| type | The type of the comment | `user` \| `alert` | -| comment | The comment. Valid only when type is `user`. | string | -| alertId | The alert ID. Valid only when the type is `alert` | string | -| index | The index where the alert is saved. Valid only when the type is `alert` | string | +| Property | Description | Type | +| -------- | ------------------------ | ------ | +| type | The type of the comment. | `user` | +| comment | The comment. | string | + #### `connector` | Property | Description | Type | @@ -96,3 +96,9 @@ For IBM Resilient connectors: | ------------ | ------------------------------- | -------- | | issueTypes | The issue types of the issue. | string[] | | severityCode | The severity code of the issue. | string | + +#### `settings` + +| Property | Description | Type | +| ---------- | ------------------------------ | ------- | +| syncAlerts | Turn on or off alert synching. | boolean | \ No newline at end of file From 207c8eac5c50b45212b4c34e781fe2fef90fbc44 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 25 Jan 2021 15:56:29 +0000 Subject: [PATCH 33/62] corrected terminology in PR template (#89095) We recently added a usage of `whitelist`, I've changed it to `allowlist` --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index efba93350b8fb..2a5fc914662b6 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -11,7 +11,7 @@ Delete any items that are not applicable to this PR. - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) -- [ ] If a plugin configuration key changed, check if it needs to be whitelisted in the [cloud](https://github.com/elastic/cloud) and added to the [docker list](https://github.com/elastic/kibana/blob/c29adfef29e921cc447d2a5ed06ac2047ceab552/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker) +- [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the [cloud](https://github.com/elastic/cloud) and added to the [docker list](https://github.com/elastic/kibana/blob/c29adfef29e921cc447d2a5ed06ac2047ceab552/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) From f6837a1f66db9909768afdd68bf2f0be8c3087f4 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 25 Jan 2021 15:57:50 +0000 Subject: [PATCH 34/62] made unit test more reliable (#89094) Made unit test more reliable by using resolving promises rather than timed `await`s that could be flaky when the node event loop is overwhelmed. --- .../task_manager/server/task_pool.test.ts | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/task_manager/server/task_pool.test.ts b/x-pack/plugins/task_manager/server/task_pool.test.ts index 324e376c32d95..14ad0561928f8 100644 --- a/x-pack/plugins/task_manager/server/task_pool.test.ts +++ b/x-pack/plugins/task_manager/server/task_pool.test.ts @@ -13,6 +13,7 @@ import { Logger } from '../../../../src/core/server'; import { asOk } from './lib/result_type'; import { SavedObjectsErrorHelpers } from '../../../../src/core/server'; import moment from 'moment'; +import uuid from 'uuid'; describe('TaskPool', () => { test('occupiedWorkers are a sum of running tasks', async () => { @@ -133,7 +134,7 @@ describe('TaskPool', () => { const result = await pool.run([mockTask(), taskFailedToRun, mockTask()]); expect(logger.debug).toHaveBeenCalledWith( - 'Task TaskType "shooooo" failed in attempt to run: Saved object [task/foo] not found' + `Task TaskType "shooooo" failed in attempt to run: Saved object [task/${taskFailedToRun.id}] not found` ); expect(logger.warn).not.toHaveBeenCalled(); @@ -203,26 +204,28 @@ describe('TaskPool', () => { sinon.assert.calledOnce(secondRun); }); - test.skip('run cancels expired tasks prior to running new tasks', async () => { + test('run cancels expired tasks prior to running new tasks', async () => { const logger = loggingSystemMock.create().get(); const pool = new TaskPool({ maxWorkers$: of(2), logger, }); - const readyToExpire = resolvable(); + const haltUntilWeAfterFirstRun = resolvable(); const taskHasExpired = resolvable(); + const haltTaskSoThatItCanBeCanceled = resolvable(); + const shouldRun = sinon.spy(() => Promise.resolve()); const shouldNotRun = sinon.spy(() => Promise.resolve()); const now = new Date(); const result = await pool.run([ { - ...mockTask(), + ...mockTask({ id: '1' }), async run() { - await readyToExpire; + await haltUntilWeAfterFirstRun; this.isExpired = true; taskHasExpired.resolve(); - await sleep(10); + await haltTaskSoThatItCanBeCanceled; return asOk({ state: {} }); }, get expiration() { @@ -235,9 +238,10 @@ describe('TaskPool', () => { cancel: shouldRun, }, { - ...mockTask(), + ...mockTask({ id: '2' }), async run() { - await sleep(10); + // halt here so that we can verify that this task is counted in `occupiedWorkers` + await haltUntilWeAfterFirstRun; return asOk({ state: {} }); }, cancel: shouldNotRun, @@ -248,16 +252,19 @@ describe('TaskPool', () => { expect(pool.occupiedWorkers).toEqual(2); expect(pool.availableWorkers).toEqual(0); - readyToExpire.resolve(); + // release first stage in task so that it has time to expire, but not complete + haltUntilWeAfterFirstRun.resolve(); await taskHasExpired; - expect(await pool.run([{ ...mockTask() }])).toBeTruthy(); + expect(await pool.run([{ ...mockTask({ id: '3' }) }])).toBeTruthy(); sinon.assert.calledOnce(shouldRun); sinon.assert.notCalled(shouldNotRun); - expect(pool.occupiedWorkers).toEqual(2); - expect(pool.availableWorkers).toEqual(0); + expect(pool.occupiedWorkers).toEqual(1); + expect(pool.availableWorkers).toEqual(1); + + haltTaskSoThatItCanBeCanceled.resolve(); expect(logger.warn).toHaveBeenCalledWith( `Cancelling task TaskType "shooooo" as it expired at ${now.toISOString()} after running for 05m 30s (with timeout set at 5m).` @@ -355,10 +362,10 @@ describe('TaskPool', () => { }); } - function mockTask() { + function mockTask(overrides = {}) { return { isExpired: false, - id: 'foo', + id: uuid.v4(), cancel: async () => undefined, markTaskAsRunning: jest.fn(async () => true), run: mockRun(), @@ -377,6 +384,7 @@ describe('TaskPool', () => { createTaskRunner: jest.fn(), }; }, + ...overrides, }; } }); From e251ff4be593d45d6094efb414b1dccecff42072 Mon Sep 17 00:00:00 2001 From: Oliver Gupte Date: Mon, 25 Jan 2021 08:05:32 -0800 Subject: [PATCH 35/62] [APM] Renames significant terms feature to "Correlations" (#88974) (#89028) * [APM] Renames significant terms feature to "Correlations" (#88974) * fix capitalizations Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/apm/common/ui_settings_keys.ts | 2 +- .../app/Correlations/LatencyCorrelations.tsx | 2 +- .../apm/public/components/app/Correlations/index.tsx | 10 +++++----- x-pack/plugins/apm/server/ui_settings.ts | 9 ++++----- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/apm/common/ui_settings_keys.ts b/x-pack/plugins/apm/common/ui_settings_keys.ts index ffc2a2ef21fe9..38922fa445a47 100644 --- a/x-pack/plugins/apm/common/ui_settings_keys.ts +++ b/x-pack/plugins/apm/common/ui_settings_keys.ts @@ -4,5 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export const enableSignificantTerms = 'apm:enableSignificantTerms'; +export const enableCorrelations = 'apm:enableCorrelations'; export const enableServiceOverview = 'apm:enableServiceOverview'; diff --git a/x-pack/plugins/apm/public/components/app/Correlations/LatencyCorrelations.tsx b/x-pack/plugins/apm/public/components/app/Correlations/LatencyCorrelations.tsx index b2d88c4c3849b..438303110fbc4 100644 --- a/x-pack/plugins/apm/public/components/app/Correlations/LatencyCorrelations.tsx +++ b/x-pack/plugins/apm/public/components/app/Correlations/LatencyCorrelations.tsx @@ -128,7 +128,7 @@ export function LatencyCorrelations() { - View significant terms + View correlations @@ -62,7 +62,7 @@ export function Correlations() { > -

Significant terms

+

Correlations

@@ -88,7 +88,7 @@ export function Correlations() { iconType="alert" >

- Significant terms is an experimental feature and in active + Correlations is an experimental feature and in active development. Bugs and surprises are to be expected but let us know your feedback so we can improve it.

diff --git a/x-pack/plugins/apm/server/ui_settings.ts b/x-pack/plugins/apm/server/ui_settings.ts index c86fb636b5a1a..e9bb747280fc7 100644 --- a/x-pack/plugins/apm/server/ui_settings.ts +++ b/x-pack/plugins/apm/server/ui_settings.ts @@ -8,7 +8,7 @@ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; import { UiSettingsParams } from '../../../../src/core/types'; import { - enableSignificantTerms, + enableCorrelations, enableServiceOverview, } from '../common/ui_settings_keys'; @@ -16,17 +16,16 @@ import { * uiSettings definitions for APM. */ export const uiSettings: Record> = { - [enableSignificantTerms]: { + [enableCorrelations]: { category: ['observability'], name: i18n.translate('xpack.apm.enableCorrelationsExperimentName', { - defaultMessage: 'APM Significant terms (Platinum required)', + defaultMessage: 'APM correlations (Platinum required)', }), value: false, description: i18n.translate( 'xpack.apm.enableCorrelationsExperimentDescription', { - defaultMessage: - 'Enable the experimental Significant terms feature in APM', + defaultMessage: 'Enable the experimental correlations feature in APM', } ), schema: schema.boolean(), From 6391ef9c45409cdd47d13f5b5fb420d092562b68 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 25 Jan 2021 17:08:31 +0100 Subject: [PATCH 36/62] [Observability] Lazy load shared components (#88802) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../app/RumDashboard/UXMetrics/index.tsx | 23 ++++++++++------ .../public/components/app/header/index.tsx | 2 +- .../components/app/section/ux/index.tsx | 2 +- .../shared/core_web_vitals/index.tsx | 17 ++++-------- .../components/shared/header_menu_portal.tsx | 12 +++------ .../public/components/shared/index.tsx | 26 +++++++++++++++++++ .../public/components/shared/types.ts | 23 ++++++++++++++++ x-pack/plugins/observability/public/index.ts | 6 ++--- 8 files changed, 78 insertions(+), 33 deletions(-) create mode 100644 x-pack/plugins/observability/public/components/shared/index.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/types.ts diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/index.tsx index 392b42cba12e5..29d5750231762 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; +import React, { useContext, useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem, @@ -17,7 +17,7 @@ import { I18LABELS } from '../translations'; import { KeyUXMetrics } from './KeyUXMetrics'; import { useFetcher } from '../../../../hooks/use_fetcher'; import { useUxQuery } from '../hooks/useUxQuery'; -import { CoreVitals } from '../../../../../../observability/public'; +import { getCoreVitalsComponent } from '../../../../../../observability/public'; import { CsmSharedContext } from '../CsmSharedContext'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { getPercentileLabel } from './translations'; @@ -48,6 +48,18 @@ export function UXMetrics() { sharedData: { totalPageViews }, } = useContext(CsmSharedContext); + const CoreVitals = useMemo( + () => + getCoreVitalsComponent({ + data, + totalPageViews, + loading: status !== 'success', + displayTrafficMetric: true, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [status] + ); + return ( @@ -67,12 +79,7 @@ export function UXMetrics() { - + {CoreVitals} diff --git a/x-pack/plugins/observability/public/components/app/header/index.tsx b/x-pack/plugins/observability/public/components/app/header/index.tsx index b195bb52e7ed2..097871fe020e5 100644 --- a/x-pack/plugins/observability/public/components/app/header/index.tsx +++ b/x-pack/plugins/observability/public/components/app/header/index.tsx @@ -17,7 +17,7 @@ import { i18n } from '@kbn/i18n'; import React, { ReactNode } from 'react'; import styled from 'styled-components'; import { usePluginContext } from '../../../hooks/use_plugin_context'; -import { HeaderMenuPortal } from '../../shared/header_menu_portal'; +import HeaderMenuPortal from '../../shared/header_menu_portal'; const Container = styled.div<{ color: string }>` background: ${(props) => props.color}; diff --git a/x-pack/plugins/observability/public/components/app/section/ux/index.tsx b/x-pack/plugins/observability/public/components/app/section/ux/index.tsx index 43f1072d06fc2..7074a895d058b 100644 --- a/x-pack/plugins/observability/public/components/app/section/ux/index.tsx +++ b/x-pack/plugins/observability/public/components/app/section/ux/index.tsx @@ -12,7 +12,7 @@ import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; import { useHasData } from '../../../../hooks/use_has_data'; import { useTimeRange } from '../../../../hooks/use_time_range'; import { UXHasDataResponse } from '../../../../typings'; -import { CoreVitals } from '../../../shared/core_web_vitals'; +import CoreVitals from '../../../shared/core_web_vitals'; interface Props { bucketSize: string; diff --git a/x-pack/plugins/observability/public/components/shared/core_web_vitals/index.tsx b/x-pack/plugins/observability/public/components/shared/core_web_vitals/index.tsx index f573c8cfc1f97..7d40ce089cec4 100644 --- a/x-pack/plugins/observability/public/components/shared/core_web_vitals/index.tsx +++ b/x-pack/plugins/observability/public/components/shared/core_web_vitals/index.tsx @@ -16,6 +16,7 @@ import { import { CoreVitalItem } from './core_vital_item'; import { WebCoreVitalsTitle } from './web_core_vitals_title'; import { ServiceName } from './service_name'; +import { CoreVitalProps } from '../types'; export interface UXMetrics { cls: number | null; @@ -29,7 +30,7 @@ export interface UXMetrics { clsRanks: number[]; } -export function formatToSec(value?: number | string, fromUnit = 'MicroSec'): string { +function formatToSec(value?: number | string, fromUnit = 'MicroSec'): string { const valueInMs = Number(value ?? 0) / (fromUnit === 'MicroSec' ? 1000 : 1); if (valueInMs < 1000) { @@ -51,23 +52,15 @@ const CoreVitalsThresholds = { CLS: { good: '0.1', bad: '0.25' }, }; -interface Props { - loading: boolean; - data?: UXMetrics | null; - displayServiceName?: boolean; - serviceName?: string; - totalPageViews?: number; - displayTrafficMetric?: boolean; -} - -export function CoreVitals({ +// eslint-disable-next-line import/no-default-export +export default function CoreVitals({ data, loading, displayServiceName, serviceName, totalPageViews, displayTrafficMetric = false, -}: Props) { +}: CoreVitalProps) { const { lcp, lcpRanks, fid, fidRanks, cls, clsRanks, coreVitalPages } = data || {}; return ( diff --git a/x-pack/plugins/observability/public/components/shared/header_menu_portal.tsx b/x-pack/plugins/observability/public/components/shared/header_menu_portal.tsx index ca03eb6ddb45a..e209e830d0f37 100644 --- a/x-pack/plugins/observability/public/components/shared/header_menu_portal.tsx +++ b/x-pack/plugins/observability/public/components/shared/header_menu_portal.tsx @@ -4,17 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { ReactNode, useEffect, useMemo } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { createPortalNode, InPortal, OutPortal } from 'react-reverse-portal'; -import { AppMountParameters } from '../../../../../../src/core/public'; import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public'; +import { HeaderMenuPortalProps } from './types'; -interface HeaderMenuPortalProps { - children: ReactNode; - setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; -} - -export function HeaderMenuPortal({ children, setHeaderActionMenu }: HeaderMenuPortalProps) { +// eslint-disable-next-line import/no-default-export +export default function HeaderMenuPortal({ children, setHeaderActionMenu }: HeaderMenuPortalProps) { const portalNode = useMemo(() => createPortalNode(), []); useEffect(() => { diff --git a/x-pack/plugins/observability/public/components/shared/index.tsx b/x-pack/plugins/observability/public/components/shared/index.tsx new file mode 100644 index 0000000000000..6e3835129beb2 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/index.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { lazy, Suspense } from 'react'; +import { CoreVitalProps, HeaderMenuPortalProps } from './types'; + +export function getCoreVitalsComponent(props: CoreVitalProps) { + const CoreVitalsLazy = lazy(() => import('./core_web_vitals/index')); + return ( + + + + ); +} + +export function HeaderMenuPortal(props: HeaderMenuPortalProps) { + const HeaderMenuPortalLazy = lazy(() => import('./header_menu_portal')); + return ( + + + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/types.ts b/x-pack/plugins/observability/public/components/shared/types.ts new file mode 100644 index 0000000000000..9039f444f550f --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ReactNode } from 'react'; +import { AppMountParameters } from '../../../../../../src/core/public'; +import { UXMetrics } from './core_web_vitals'; + +export interface HeaderMenuPortalProps { + children: ReactNode; + setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; +} + +export interface CoreVitalProps { + loading: boolean; + data?: UXMetrics | null; + displayServiceName?: boolean; + serviceName?: string; + totalPageViews?: number; + displayTrafficMetric?: boolean; +} diff --git a/x-pack/plugins/observability/public/index.ts b/x-pack/plugins/observability/public/index.ts index 22cc5faf23967..c052541956c13 100644 --- a/x-pack/plugins/observability/public/index.ts +++ b/x-pack/plugins/observability/public/index.ts @@ -6,8 +6,7 @@ import { PluginInitializerContext, PluginInitializer } from 'kibana/public'; import { Plugin, ObservabilityPluginSetup, ObservabilityPluginStart } from './plugin'; -export { HeaderMenuPortal } from './components/shared/header_menu_portal'; -export { ObservabilityPluginSetup, ObservabilityPluginStart }; +export type { ObservabilityPluginSetup, ObservabilityPluginStart }; export const plugin: PluginInitializer = ( context: PluginInitializerContext @@ -17,7 +16,8 @@ export const plugin: PluginInitializer Date: Mon, 25 Jan 2021 16:20:14 +0000 Subject: [PATCH 37/62] [ML] Add ML deep links to navigational search (#88958) * [ML] Add ML deep links to navigational search * [ML] Refactor register helper files * [ML] Edit import in search_deep_links * [ML] Move register_feature out of register_helper * [ML] Add comment about registerFeature Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/ml/public/plugin.ts | 37 +++--- .../ml/public/register_helper/index.ts | 10 ++ .../register_search_links/index.ts} | 5 +- .../register_search_links.ts | 27 +++++ .../search_deep_links.ts | 110 ++++++++++++++++++ 5 files changed, 172 insertions(+), 17 deletions(-) create mode 100644 x-pack/plugins/ml/public/register_helper/index.ts rename x-pack/plugins/ml/public/{register_helper.ts => register_helper/register_search_links/index.ts} (51%) create mode 100644 x-pack/plugins/ml/public/register_helper/register_search_links/register_search_links.ts create mode 100644 x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index 1cc69ac2239ab..7c32671be93c4 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -70,7 +70,7 @@ export interface MlSetupDependencies { export type MlCoreSetup = CoreSetup; export class MlPlugin implements Plugin { - private appUpdater = new BehaviorSubject(() => ({})); + private appUpdater$ = new BehaviorSubject(() => ({})); private urlGenerator: undefined | UrlGeneratorContract; constructor(private initializerContext: PluginInitializerContext) {} @@ -85,7 +85,7 @@ export class MlPlugin implements Plugin { euiIconType: PLUGIN_ICON_SOLUTION, appRoute: '/app/ml', category: DEFAULT_APP_CATEGORIES.kibana, - updater$: this.appUpdater, + updater$: this.appUpdater$, mount: async (params: AppMountParameters) => { const [coreStart, pluginsStart] = await core.getStartServices(); const kibanaVersion = this.initializerContext.env.packageInfo.version; @@ -133,23 +133,34 @@ export class MlPlugin implements Plugin { }); } else { // if ml is disabled in elasticsearch, disable ML in kibana - this.appUpdater.next(() => ({ + this.appUpdater$.next(() => ({ status: AppStatus.inaccessible, })); } // register various ML plugin features which require a full license - const { registerEmbeddables, registerManagementSection, registerMlUiActions } = await import( - './register_helper' - ); - - if (isMlEnabled(license) && isFullLicense(license)) { - const canManageMLJobs = capabilities.management?.insightsAndAlerting?.jobsListLink ?? false; - if (canManageMLJobs && pluginsSetup.management !== undefined) { - registerManagementSection(pluginsSetup.management, core).enable(); + // note including registerFeature in register_helper would cause the page bundle size to increase significantly + const { + registerEmbeddables, + registerManagementSection, + registerMlUiActions, + registerSearchLinks, + } = await import('./register_helper'); + + const mlEnabled = isMlEnabled(license); + const fullLicense = isFullLicense(license); + if (mlEnabled) { + registerSearchLinks(this.appUpdater$, fullLicense); + + if (fullLicense) { + const canManageMLJobs = + capabilities.management?.insightsAndAlerting?.jobsListLink ?? false; + if (canManageMLJobs && pluginsSetup.management !== undefined) { + registerManagementSection(pluginsSetup.management, core).enable(); + } + registerEmbeddables(pluginsSetup.embeddable, core); + registerMlUiActions(pluginsSetup.uiActions, core); } - registerEmbeddables(pluginsSetup.embeddable, core); - registerMlUiActions(pluginsSetup.uiActions, core); } }); diff --git a/x-pack/plugins/ml/public/register_helper/index.ts b/x-pack/plugins/ml/public/register_helper/index.ts new file mode 100644 index 0000000000000..8e62b6562520a --- /dev/null +++ b/x-pack/plugins/ml/public/register_helper/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { registerEmbeddables } from '../embeddables'; +export { registerManagementSection } from '../application/management'; +export { registerMlUiActions } from '../ui_actions'; +export { registerSearchLinks } from './register_search_links'; diff --git a/x-pack/plugins/ml/public/register_helper.ts b/x-pack/plugins/ml/public/register_helper/register_search_links/index.ts similarity index 51% rename from x-pack/plugins/ml/public/register_helper.ts rename to x-pack/plugins/ml/public/register_helper/register_search_links/index.ts index 50ec53a10ece9..e1912c7ebabeb 100644 --- a/x-pack/plugins/ml/public/register_helper.ts +++ b/x-pack/plugins/ml/public/register_helper/register_search_links/index.ts @@ -4,7 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { registerEmbeddables } from './embeddables'; -export { registerFeature } from './register_feature'; -export { registerManagementSection } from './application/management'; -export { registerMlUiActions } from './ui_actions'; +export { registerSearchLinks } from './register_search_links'; diff --git a/x-pack/plugins/ml/public/register_helper/register_search_links/register_search_links.ts b/x-pack/plugins/ml/public/register_helper/register_search_links/register_search_links.ts new file mode 100644 index 0000000000000..2df7e8140698a --- /dev/null +++ b/x-pack/plugins/ml/public/register_helper/register_search_links/register_search_links.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { BehaviorSubject } from 'rxjs'; + +import { AppUpdater } from 'src/core/public'; +import { getSearchDeepLinks } from './search_deep_links'; + +export function registerSearchLinks( + appUpdater: BehaviorSubject, + isFullLicense: boolean +) { + appUpdater.next(() => ({ + meta: { + keywords: [ + i18n.translate('xpack.ml.keyword.ml', { + defaultMessage: 'ML', + }), + ], + searchDeepLinks: getSearchDeepLinks(isFullLicense), + }, + })); +} diff --git a/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts b/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts new file mode 100644 index 0000000000000..7108fb7af5670 --- /dev/null +++ b/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts @@ -0,0 +1,110 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +import type { AppSearchDeepLink } from 'src/core/public'; +import { ML_PAGES } from '../../../common/constants/ml_url_generator'; + +const OVERVIEW_LINK_SEARCH_DEEP_LINK: AppSearchDeepLink = { + id: 'mlOverviewSearchDeepLink', + title: i18n.translate('xpack.ml.searchDeepLink.overview', { + defaultMessage: 'Overview', + }), + path: `/${ML_PAGES.OVERVIEW}`, +}; + +const ANOMALY_DETECTION_SEARCH_DEEP_LINK: AppSearchDeepLink = { + id: 'mlAnomalyDetectionSearchDeepLink', + title: i18n.translate('xpack.ml.searchDeepLink.anomalyDetection', { + defaultMessage: 'Anomaly Detection', + }), + path: `/${ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE}`, +}; + +const DATA_FRAME_ANALYTICS_SEARCH_DEEP_LINK: AppSearchDeepLink = { + id: 'mlDataFrameAnalyticsSearchDeepLink', + title: i18n.translate('xpack.ml.searchDeepLink.dataFrameAnalytics', { + defaultMessage: 'Data Frame Analytics', + }), + path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE}`, + searchDeepLinks: [ + { + id: 'mlTrainedModelsSearchDeepLink', + title: i18n.translate('xpack.ml.searchDeepLink.trainedModels', { + defaultMessage: 'Trained Models', + }), + path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_MODELS_MANAGE}`, + }, + ], +}; + +const DATA_VISUALIZER_SEARCH_DEEP_LINK: AppSearchDeepLink = { + id: 'mlDataVisualizerSearchDeepLink', + title: i18n.translate('xpack.ml.searchDeepLink.dataVisualizer', { + defaultMessage: 'Data Visualizer', + }), + path: `/${ML_PAGES.DATA_VISUALIZER}`, +}; + +const FILE_UPLOAD_SEARCH_DEEP_LINK: AppSearchDeepLink = { + id: 'mlFileUploadSearchDeepLink', + title: i18n.translate('xpack.ml.searchDeepLink.fileUpload', { + defaultMessage: 'File Upload', + }), + path: `/${ML_PAGES.DATA_VISUALIZER_FILE}`, +}; + +const INDEX_DATA_VISUALIZER_SEARCH_DEEP_LINK: AppSearchDeepLink = { + id: 'mlIndexDataVisualizerSearchDeepLink', + title: i18n.translate('xpack.ml.searchDeepLink.indexDataVisualizer', { + defaultMessage: 'Index Data Visualizer', + }), + path: `/${ML_PAGES.DATA_VISUALIZER_INDEX_SELECT}`, +}; + +const SETTINGS_SEARCH_DEEP_LINK: AppSearchDeepLink = { + id: 'mlSettingsSearchDeepLink', + title: i18n.translate('xpack.ml.searchDeepLink.settings', { + defaultMessage: 'Settings', + }), + path: `/${ML_PAGES.SETTINGS}`, + searchDeepLinks: [ + { + id: 'mlCalendarSettingsSearchDeepLink', + title: i18n.translate('xpack.ml.searchDeepLink.calendarSettings', { + defaultMessage: 'Calendars', + }), + path: `/${ML_PAGES.CALENDARS_MANAGE}`, + }, + { + id: 'mlFilterListsSettingsSearchDeepLink', + title: i18n.translate('xpack.ml.searchDeepLink.filterListsSettings', { + defaultMessage: 'Filter Lists', + }), + path: `/${ML_PAGES.SETTINGS}`, // Link to settings page as read only users cannot view filter lists. + }, + ], +}; + +export function getSearchDeepLinks(isFullLicense: boolean) { + const deepLinks: AppSearchDeepLink[] = [ + DATA_VISUALIZER_SEARCH_DEEP_LINK, + FILE_UPLOAD_SEARCH_DEEP_LINK, + INDEX_DATA_VISUALIZER_SEARCH_DEEP_LINK, + ]; + + if (isFullLicense === true) { + deepLinks.push( + OVERVIEW_LINK_SEARCH_DEEP_LINK, + ANOMALY_DETECTION_SEARCH_DEEP_LINK, + DATA_FRAME_ANALYTICS_SEARCH_DEEP_LINK, + SETTINGS_SEARCH_DEEP_LINK + ); + } + + return deepLinks; +} From 43db7e365ff53e9014934910c34ee647eac29955 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Mon, 25 Jan 2021 11:27:59 -0500 Subject: [PATCH 38/62] [Search Profiler] Migrate server to new es-js client (#88725) --- .../searchprofiler/server/routes/profile.ts | 20 +++---- x-pack/test/api_integration/apis/index.ts | 1 + .../apis/searchprofiler/index.ts | 13 +++++ .../apis/searchprofiler/searchprofiler.ts | 56 +++++++++++++++++++ 4 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 x-pack/test/api_integration/apis/searchprofiler/index.ts create mode 100644 x-pack/test/api_integration/apis/searchprofiler/searchprofiler.ts diff --git a/x-pack/plugins/searchprofiler/server/routes/profile.ts b/x-pack/plugins/searchprofiler/server/routes/profile.ts index 914c688a080f8..87f2ec1df1c92 100644 --- a/x-pack/plugins/searchprofiler/server/routes/profile.ts +++ b/x-pack/plugins/searchprofiler/server/routes/profile.ts @@ -27,10 +27,6 @@ export const register = ({ router, getLicenseStatus, log }: RouteDependencies) = }); } - const { - core: { elasticsearch }, - } = ctx; - const { body: { query, index }, } = request; @@ -46,21 +42,25 @@ export const register = ({ router, getLicenseStatus, log }: RouteDependencies) = body: JSON.stringify(parsed, null, 2), }; try { - const resp = await elasticsearch.legacy.client.callAsCurrentUser('search', body); + const client = ctx.core.elasticsearch.client.asCurrentUser; + const resp = await client.search(body); + return response.ok({ body: { ok: true, - resp, + resp: resp.body, }, }); } catch (err) { log.error(err); + const { statusCode, body: errorBody } = err; + return response.customError({ - statusCode: err.status || 500, - body: err.body + statusCode: statusCode || 500, + body: errorBody ? { - message: err.message, - attributes: err.body, + message: errorBody.error?.reason, + attributes: errorBody, } : err, }); diff --git a/x-pack/test/api_integration/apis/index.ts b/x-pack/test/api_integration/apis/index.ts index 6b6326df017aa..2cd2654cffe3e 100644 --- a/x-pack/test/api_integration/apis/index.ts +++ b/x-pack/test/api_integration/apis/index.ts @@ -33,5 +33,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./transform')); loadTestFile(require.resolve('./lists')); loadTestFile(require.resolve('./upgrade_assistant')); + loadTestFile(require.resolve('./searchprofiler')); }); } diff --git a/x-pack/test/api_integration/apis/searchprofiler/index.ts b/x-pack/test/api_integration/apis/searchprofiler/index.ts new file mode 100644 index 0000000000000..36794feb00d1b --- /dev/null +++ b/x-pack/test/api_integration/apis/searchprofiler/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Search Profiler', () => { + loadTestFile(require.resolve('./searchprofiler')); + }); +} diff --git a/x-pack/test/api_integration/apis/searchprofiler/searchprofiler.ts b/x-pack/test/api_integration/apis/searchprofiler/searchprofiler.ts new file mode 100644 index 0000000000000..041cfb82520b4 --- /dev/null +++ b/x-pack/test/api_integration/apis/searchprofiler/searchprofiler.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +const API_BASE_PATH = '/api/searchprofiler'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('Profile', () => { + it('should return profile results for a valid index', async () => { + const payload = { + index: '_all', + query: { + query: { + match_all: {}, + }, + }, + }; + + const { body } = await supertest + .post(`${API_BASE_PATH}/profile`) + .set('kbn-xsrf', 'xxx') + .set('Content-Type', 'application/json;charset=UTF-8') + .send(payload) + .expect(200); + + expect(body.ok).to.eql(true); + }); + + it('should return error for invalid index', async () => { + const payloadWithInvalidIndex = { + index: 'index_does_not_exist', + query: { + query: { + match_all: {}, + }, + }, + }; + + const { body } = await supertest + .post(`${API_BASE_PATH}/execute`) + .set('kbn-xsrf', 'xxx') + .set('Content-Type', 'application/json;charset=UTF-8') + .send(payloadWithInvalidIndex) + .expect(404); + + expect(body.error).to.eql('Not Found'); + }); + }); +} From 592d79c210f7a614c37f1773ae4d4feef003f0e8 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 25 Jan 2021 17:07:13 +0000 Subject: [PATCH 39/62] skip failing es promotion suite (#89180) --- .../cross_cluster_replication/feature_controls/ccr_security.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/ccr_security.ts b/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/ccr_security.ts index 6b4b9c61151ba..4e234dd8e5501 100644 --- a/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/ccr_security.ts +++ b/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/ccr_security.ts @@ -13,7 +13,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const appsMenu = getService('appsMenu'); const managementMenu = getService('managementMenu'); - describe('security', () => { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/89180 + describe.skip('security', () => { before(async () => { await esArchiver.load('empty_kibana'); await PageObjects.common.navigateToApp('home'); From 342f9fbac15ce118ad088d2908c263d36ec23be4 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 25 Jan 2021 17:11:52 +0000 Subject: [PATCH 40/62] skip failing es promotion suite (#89181) --- .../feature_controls/remote_clusters_security.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/remote_clusters/feature_controls/remote_clusters_security.ts b/x-pack/test/functional/apps/remote_clusters/feature_controls/remote_clusters_security.ts index b1edc74607161..e6b5a7ac77d7c 100644 --- a/x-pack/test/functional/apps/remote_clusters/feature_controls/remote_clusters_security.ts +++ b/x-pack/test/functional/apps/remote_clusters/feature_controls/remote_clusters_security.ts @@ -13,7 +13,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const appsMenu = getService('appsMenu'); const managementMenu = getService('managementMenu'); - describe('security', () => { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/89181 + describe.skip('security', () => { before(async () => { await esArchiver.load('empty_kibana'); await PageObjects.common.navigateToApp('home'); From edb5e35151a8fb5b168cb600cf3fdda94407177e Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Mon, 25 Jan 2021 13:26:53 -0500 Subject: [PATCH 41/62] [Painless Lab] Update server to use new es-js client (#88704) --- .../errors/handle_es_error.ts | 12 ++++- .../painless_lab/server/routes/api/execute.ts | 23 ++++----- .../painless_lab/server/shared_imports.ts | 2 +- x-pack/test/api_integration/apis/index.ts | 1 + .../apis/painless_lab/index.ts | 13 +++++ .../apis/painless_lab/painless_lab.ts | 49 +++++++++++++++++++ 6 files changed, 87 insertions(+), 13 deletions(-) create mode 100644 x-pack/test/api_integration/apis/painless_lab/index.ts create mode 100644 x-pack/test/api_integration/apis/painless_lab/painless_lab.ts diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/errors/handle_es_error.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/errors/handle_es_error.ts index b7f7a5abb82b0..e2e1d7f05851c 100644 --- a/src/plugins/es_ui_shared/__packages_do_not_import__/errors/handle_es_error.ts +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/errors/handle_es_error.ts @@ -13,14 +13,24 @@ import { IKibanaResponse, KibanaResponseFactory } from 'kibana/server'; interface EsErrorHandlerParams { error: ApiError; response: KibanaResponseFactory; + handleCustomError?: () => IKibanaResponse; } /* * For errors returned by the new elasticsearch js client. */ -export const handleEsError = ({ error, response }: EsErrorHandlerParams): IKibanaResponse => { +export const handleEsError = ({ + error, + response, + handleCustomError, +}: EsErrorHandlerParams): IKibanaResponse => { // error.name is slightly better in terms of performance, since all errors now have name property if (error.name === 'ResponseError') { + // The consumer may sometimes want to provide a custom response + if (typeof handleCustomError === 'function') { + return handleCustomError(); + } + const { statusCode, body } = error as ResponseError; return response.customError({ statusCode, diff --git a/x-pack/plugins/painless_lab/server/routes/api/execute.ts b/x-pack/plugins/painless_lab/server/routes/api/execute.ts index 7bb2d9f93c6fc..44529d7bacbd4 100644 --- a/x-pack/plugins/painless_lab/server/routes/api/execute.ts +++ b/x-pack/plugins/painless_lab/server/routes/api/execute.ts @@ -7,7 +7,7 @@ import { schema } from '@kbn/config-schema'; import { API_BASE_PATH } from '../../../common/constants'; import { RouteDependencies } from '../../types'; -import { isEsError } from '../../shared_imports'; +import { handleEsError } from '../../shared_imports'; const bodySchema = schema.string(); @@ -23,23 +23,24 @@ export function registerExecuteRoute({ router, license }: RouteDependencies) { const body = req.body; try { - const callAsCurrentUser = ctx.core.elasticsearch.legacy.client.callAsCurrentUser; - const response = await callAsCurrentUser('scriptsPainlessExecute', { + const client = ctx.core.elasticsearch.client.asCurrentUser; + const response = await client.scriptsPainlessExecute({ body, }); return res.ok({ - body: response, + body: response.body, }); - } catch (e) { - if (isEsError(e)) { - // Assume invalid painless script was submitted - // Return 200 with error object + } catch (error) { + // Assume invalid painless script was submitted + // Return 200 with error object + const handleCustomError = () => { return res.ok({ - body: e.body, + body: error.body, }); - } - return res.internalError({ body: e }); + }; + + return handleEsError({ error, response: res, handleCustomError }); } }) ); diff --git a/x-pack/plugins/painless_lab/server/shared_imports.ts b/x-pack/plugins/painless_lab/server/shared_imports.ts index 454beda5394c7..068cddcee4c86 100644 --- a/x-pack/plugins/painless_lab/server/shared_imports.ts +++ b/x-pack/plugins/painless_lab/server/shared_imports.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { isEsError } from '../../../../src/plugins/es_ui_shared/server'; +export { handleEsError } from '../../../../src/plugins/es_ui_shared/server'; diff --git a/x-pack/test/api_integration/apis/index.ts b/x-pack/test/api_integration/apis/index.ts index 2cd2654cffe3e..ea42b583fc00e 100644 --- a/x-pack/test/api_integration/apis/index.ts +++ b/x-pack/test/api_integration/apis/index.ts @@ -34,5 +34,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./lists')); loadTestFile(require.resolve('./upgrade_assistant')); loadTestFile(require.resolve('./searchprofiler')); + loadTestFile(require.resolve('./painless_lab')); }); } diff --git a/x-pack/test/api_integration/apis/painless_lab/index.ts b/x-pack/test/api_integration/apis/painless_lab/index.ts new file mode 100644 index 0000000000000..7ccd7c5691e56 --- /dev/null +++ b/x-pack/test/api_integration/apis/painless_lab/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Painless Lab', () => { + loadTestFile(require.resolve('./painless_lab')); + }); +} diff --git a/x-pack/test/api_integration/apis/painless_lab/painless_lab.ts b/x-pack/test/api_integration/apis/painless_lab/painless_lab.ts new file mode 100644 index 0000000000000..c78ceaaa81437 --- /dev/null +++ b/x-pack/test/api_integration/apis/painless_lab/painless_lab.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +const API_BASE_PATH = '/api/painless_lab'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('Painless Lab', function () { + describe('Execute', () => { + it('should execute a valid painless script', async () => { + const script = + '"{\\n \\"script\\": {\\n \\"source\\": \\"return true;\\",\\n \\"params\\": {\\n \\"string_parameter\\": \\"string value\\",\\n \\"number_parameter\\": 1.5,\\n \\"boolean_parameter\\": true\\n}\\n }\\n}"'; + + const { body } = await supertest + .post(`${API_BASE_PATH}/execute`) + .set('kbn-xsrf', 'xxx') + .set('Content-Type', 'application/json;charset=UTF-8') + .send(script) + .expect(200); + + expect(body).to.eql({ + result: 'true', + }); + }); + + it('should return error response for invalid painless script', async () => { + const invalidScript = + '"{\\n \\"script\\": {\\n \\"source\\": \\"foobar\\",\\n \\"params\\": {\\n \\"string_parameter\\": \\"string value\\",\\n \\"number_parameter\\": 1.5,\\n \\"boolean_parameter\\": true\\n}\\n }\\n}"'; + + const { body } = await supertest + .post(`${API_BASE_PATH}/execute`) + .set('kbn-xsrf', 'xxx') + .set('Content-Type', 'application/json;charset=UTF-8') + .send(invalidScript) + .expect(200); + + expect(body.error).to.not.be(undefined); + expect(body.error.reason).to.eql('compile error'); + }); + }); + }); +} From 7d50fbbf6d8ab748e439503a554014144c6a7f4c Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Mon, 25 Jan 2021 13:57:56 -0500 Subject: [PATCH 42/62] [CI] [TeamCity] Build master and 7.x hourly, others daily/on merge (#89184) --- .teamcity/src/Common.kt | 8 +++++++ .teamcity/src/builds/DailyCi.kt | 37 +++++++++++++++++++++++++++++++ .teamcity/src/builds/OnMergeCi.kt | 34 ++++++++++++++++++++++++++++ .teamcity/src/projects/Kibana.kt | 12 +++++++++- 4 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 .teamcity/src/builds/DailyCi.kt create mode 100644 .teamcity/src/builds/OnMergeCi.kt diff --git a/.teamcity/src/Common.kt b/.teamcity/src/Common.kt index 2e5357541bfe7..de3f96a5c790f 100644 --- a/.teamcity/src/Common.kt +++ b/.teamcity/src/Common.kt @@ -22,6 +22,14 @@ fun isReportingEnabled(): Boolean { return ENABLE_REPORTING; } +// master and 7.x get committed to so often, we only want to run full CI for them hourly +// but for other branches, we can run daily and on merge +fun isHourlyOnlyBranch(): Boolean { + val branch = getProjectBranch() + + return branch == "master" || branch.matches("""^[0-9]+\.x$""".toRegex()) +} + fun makeSafeId(id: String): String { return id.replace(Regex("[^a-zA-Z0-9_]"), "_") } diff --git a/.teamcity/src/builds/DailyCi.kt b/.teamcity/src/builds/DailyCi.kt new file mode 100644 index 0000000000000..9a8f25f5ba014 --- /dev/null +++ b/.teamcity/src/builds/DailyCi.kt @@ -0,0 +1,37 @@ +package builds + +import addSlackNotifications +import areTriggersEnabled +import dependsOn +import getProjectBranch +import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType +import jetbrains.buildServer.configs.kotlin.v2019_2.FailureAction +import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.schedule + +object DailyCi : BuildType({ + id("Daily_CI") + name = "Daily CI" + description = "Runs everything in CI, daily" + type = Type.COMPOSITE + paused = !areTriggersEnabled() + + triggers { + schedule { + schedulingPolicy = cron { + hours = "0" + minutes = "0" + } + branchFilter = "refs/heads/${getProjectBranch()}" + triggerBuild = always() + withPendingChangesOnly = false + } + } + + dependsOn( + FullCi + ) { + onDependencyCancel = FailureAction.ADD_PROBLEM + } + + addSlackNotifications() +}) diff --git a/.teamcity/src/builds/OnMergeCi.kt b/.teamcity/src/builds/OnMergeCi.kt new file mode 100644 index 0000000000000..174b73d53de61 --- /dev/null +++ b/.teamcity/src/builds/OnMergeCi.kt @@ -0,0 +1,34 @@ +package builds + +import addSlackNotifications +import areTriggersEnabled +import dependsOn +import getProjectBranch +import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType +import jetbrains.buildServer.configs.kotlin.v2019_2.FailureAction +import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.vcs + +object OnMergeCi : BuildType({ + id("OnMerge_CI") + name = "On Merge CI" + description = "Runs everything in CI, on each commit" + type = Type.COMPOSITE + paused = !areTriggersEnabled() + + maxRunningBuilds = 1 + + triggers { + vcs { + perCheckinTriggering = false + branchFilter = "refs/heads/${getProjectBranch()}" + } + } + + dependsOn( + FullCi + ) { + onDependencyCancel = FailureAction.ADD_PROBLEM + } + + addSlackNotifications() +}) diff --git a/.teamcity/src/projects/Kibana.kt b/.teamcity/src/projects/Kibana.kt index fe04d4f5ab36e..5cddcf18e067f 100644 --- a/.teamcity/src/projects/Kibana.kt +++ b/.teamcity/src/projects/Kibana.kt @@ -7,6 +7,7 @@ import builds.oss.* import builds.test.* import CloudProfile import co.elastic.teamcity.common.googleCloudProfile +import isHourlyOnlyBranch import jetbrains.buildServer.configs.kotlin.v2019_2.* import jetbrains.buildServer.configs.kotlin.v2019_2.projectFeatures.slackConnection import templates.KibanaTemplate @@ -136,7 +137,16 @@ fun Kibana(config: KibanaConfiguration = KibanaConfiguration()) : Project { buildType(FullCi) buildType(BaselineCi) - buildType(HourlyCi) + + // master and 7.x get committed to so often, we only want to run full CI for them hourly + // but for other branches, we can run daily and on merge + if (isHourlyOnlyBranch()) { + buildType(HourlyCi) + } else { + buildType(DailyCi) + buildType(OnMergeCi) + } + buildType(PullRequestCi) } From 15a45e8c38ac4b371f700285ca35591f918e3ddf Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 25 Jan 2021 12:25:52 -0700 Subject: [PATCH 43/62] [Maps] fix users without access to Maps should not have the option to create them (#88830) * [Maps] fix users without access to Maps should not have the option to create them * fix test message * wrap add geo field trigger in show capabilities check Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/maps/public/plugin.ts | 10 ++- .../apps/maps/visualize_create_menu.js | 82 ++++++++++++------- 2 files changed, 62 insertions(+), 30 deletions(-) diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 8bffea3ce5a87..690002a771601 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -160,11 +160,19 @@ export class MapsPlugin public start(core: CoreStart, plugins: MapsPluginStartDependencies): MapsStartApi { setLicensingPluginStart(plugins.licensing); - plugins.uiActions.addTriggerAction(VISUALIZE_GEO_FIELD_TRIGGER, visualizeGeoFieldAction); setStartServices(core, plugins); + // unregisters the OSS alias plugins.visualizations.unRegisterAlias(PLUGIN_ID_OSS); + if (core.application.capabilities.maps.show) { + plugins.uiActions.addTriggerAction(VISUALIZE_GEO_FIELD_TRIGGER, visualizeGeoFieldAction); + } + + if (!core.application.capabilities.maps.save) { + plugins.visualizations.unRegisterAlias(APP_ID); + } + return { createLayerDescriptors, registerLayerWizard, diff --git a/x-pack/test/functional/apps/maps/visualize_create_menu.js b/x-pack/test/functional/apps/maps/visualize_create_menu.js index 549901884d39b..f58856752dcd2 100644 --- a/x-pack/test/functional/apps/maps/visualize_create_menu.js +++ b/x-pack/test/functional/apps/maps/visualize_create_menu.js @@ -12,42 +12,66 @@ export default function ({ getService, getPageObjects }) { const security = getService('security'); describe('visualize create menu', () => { - before(async () => { - await security.testUser.setRoles( - ['test_logstash_reader', 'global_maps_all', 'geoshape_data_reader', 'global_visualize_all'], - false - ); + describe('maps visualize alias', () => { + describe('with write permission', () => { + before(async () => { + await security.testUser.setRoles(['global_maps_all', 'global_visualize_all'], false); - await PageObjects.visualize.navigateToNewVisualization(); - }); + await PageObjects.visualize.navigateToNewVisualization(); + }); - after(async () => { - await security.testUser.restoreDefaults(); - }); + it('should show maps application in create menu', async () => { + const hasMapsApp = await PageObjects.visualize.hasMapsApp(); + expect(hasMapsApp).to.equal(true); + }); - it('should show maps application in create menu', async () => { - const hasMapsApp = await PageObjects.visualize.hasMapsApp(); - expect(hasMapsApp).to.equal(true); - }); + it('should take users to Maps application when Maps is clicked', async () => { + await PageObjects.visualize.clickMapsApp(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.maps.waitForLayersToLoad(); + const doesLayerExist = await PageObjects.maps.doesLayerExist('Road map'); + expect(doesLayerExist).to.equal(true); + }); + }); - it('should not show legacy region map visualizion in create menu', async () => { - await PageObjects.visualize.clickAggBasedVisualizations(); - const hasLegecyViz = await PageObjects.visualize.hasRegionMap(); - expect(hasLegecyViz).to.equal(false); - }); + describe('without write permission', () => { + before(async () => { + await security.testUser.setRoles(['global_maps_read', 'global_visualize_all'], false); + + await PageObjects.visualize.navigateToNewVisualization(); + }); - it('should not show legacy tilemap map visualizion in create menu', async () => { - const hasLegecyViz = await PageObjects.visualize.hasTileMap(); - expect(hasLegecyViz).to.equal(false); + after(async () => { + await security.testUser.restoreDefaults(); + }); + + it('should not show maps application in create menu', async () => { + const hasMapsApp = await PageObjects.visualize.hasMapsApp(); + expect(hasMapsApp).to.equal(false); + }); + }); }); - it('should take users to Maps application when Maps is clicked', async () => { - await PageObjects.visualize.goBackToGroups(); - await PageObjects.visualize.clickMapsApp(); - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.maps.waitForLayersToLoad(); - const doesLayerExist = await PageObjects.maps.doesLayerExist('Road map'); - expect(doesLayerExist).to.equal(true); + describe('aggregion based visualizations', () => { + before(async () => { + await security.testUser.setRoles(['global_visualize_all'], false); + + await PageObjects.visualize.navigateToNewAggBasedVisualization(); + }); + + after(async () => { + await security.testUser.restoreDefaults(); + }); + + it('should not show legacy region map visualizion in create menu', async () => { + const hasLegecyViz = await PageObjects.visualize.hasRegionMap(); + expect(hasLegecyViz).to.equal(false); + }); + + it('should not show legacy tilemap map visualizion in create menu', async () => { + const hasLegecyViz = await PageObjects.visualize.hasTileMap(); + expect(hasLegecyViz).to.equal(false); + }); }); }); } From 62b32c0a2a0dc544a8c35462e00d5a278c11574b Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Mon, 25 Jan 2021 14:28:26 -0500 Subject: [PATCH 44/62] Remove onAppLeave in useEffectCleanup to prevent confirm when leaving from listing page (#89041) --- .../public/application/components/visualize_top_nav.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/visualize/public/application/components/visualize_top_nav.tsx b/src/plugins/visualize/public/application/components/visualize_top_nav.tsx index a1b6aac30d813..b0d931c6c87fa 100644 --- a/src/plugins/visualize/public/application/components/visualize_top_nav.tsx +++ b/src/plugins/visualize/public/application/components/visualize_top_nav.tsx @@ -147,6 +147,10 @@ const TopNav = ({ } return actions.default(); }); + return () => { + // reset on app leave handler so leaving from the listing page doesn't trigger a confirmation + onAppLeave((actions) => actions.default()); + }; }, [ onAppLeave, originatingApp, From e5588a129b1a0b2796822d4773176cc712dd5318 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Mon, 25 Jan 2021 13:48:35 -0600 Subject: [PATCH 45/62] Move EUI styled components integration to kibana_react (#86065) ...from xpack_legacy. Remove the duplicated typings from the observability plugin and only use the ones from kibana_react. Fixes #78248. --- .../common/eui_styled_components.tsx | 6 ++- src/plugins/kibana_react/common/index.ts | 9 ++++ src/plugins/kibana_react/kibana.json | 3 +- src/plugins/kibana_react/tsconfig.json | 9 +--- .../apm/common/service_health_status.ts | 3 +- .../ExceptionStacktrace.stories.tsx | 2 +- .../__stories__/MapTooltip.stories.tsx | 2 +- .../app/RumDashboard/utils/test_helper.tsx | 2 +- .../ServiceMap/Popover/Popover.stories.tsx | 2 +- .../Popover/service_stats_list.stories.tsx | 2 +- .../__stories__/Cytoscape.stories.tsx | 2 +- .../cytoscape_example_data.stories.tsx | 2 +- .../app/ServiceMap/cytoscape_options.ts | 2 +- .../components/app/ServiceMap/index.test.tsx | 4 +- .../use_cytoscape_event_handlers.test.tsx | 3 +- .../use_cytoscape_event_handlers.ts | 3 +- .../index.stories.tsx | 2 +- .../TransactionDetails/Distribution/index.tsx | 2 +- .../WaterfallContainer.stories.tsx | 2 +- .../service_inventory.test.tsx | 4 +- .../Timeline/Marker/AgentMarker.test.tsx | 2 +- .../shared/time_comparison/index.test.tsx | 2 +- x-pack/plugins/apm/public/hooks/use_theme.tsx | 2 +- .../selectors/latency_chart_selector.test.ts | 2 +- .../selectors/latency_chart_selectors.ts | 2 +- .../throuput_chart_selectors.test.ts | 2 +- .../selectors/throuput_chart_selectors.ts | 2 +- .../plugins/apm/public/utils/testHelpers.tsx | 2 +- .../fleet/public/applications/fleet/app.tsx | 2 +- .../inventory_models/aws_ec2/layout.tsx | 3 +- .../inventory_models/aws_rds/layout.tsx | 3 +- .../common/inventory_models/aws_s3/layout.tsx | 3 +- .../inventory_models/aws_sqs/layout.tsx | 3 +- .../inventory_models/container/layout.tsx | 3 +- .../common/inventory_models/host/layout.tsx | 3 +- .../common/inventory_models/pod/layout.tsx | 3 +- .../inventory_models/shared/layouts/aws.tsx | 3 +- .../inventory_models/shared/layouts/nginx.tsx | 3 +- .../inventory/components/expression.tsx | 2 +- .../components/expression_row.tsx | 2 +- .../infra/public/apps/common_providers.tsx | 2 +- .../autocomplete_field/autocomplete_field.tsx | 2 +- .../autocomplete_field/suggestion_item.tsx | 2 +- .../components/centered_flyout_body.tsx | 2 +- .../data_search_error_callout.stories.tsx | 2 +- .../data_search_progress.stories.tsx | 2 +- .../components/empty_states/no_data.tsx | 2 +- .../components/empty_states/no_indices.tsx | 2 +- .../infra/public/components/error_page.tsx | 2 +- .../public/components/eui/toolbar/toolbar.tsx | 2 +- .../public/components/fixed_datepicker.tsx | 2 +- .../infra/public/components/loading/index.tsx | 2 +- .../components/loading_overlay_wrapper.tsx | 2 +- .../public/components/log_stream/index.tsx | 2 +- .../log_stream/log_stream.stories.mdx | 2 +- .../quality_warning_notices.stories.tsx | 2 +- .../quality_warning_notices.tsx | 2 +- .../initial_configuration_step.stories.tsx | 2 +- .../missing_results_privileges_prompt.tsx | 2 +- .../missing_setup_privileges_prompt.tsx | 2 +- .../ml_unavailable_prompt.tsx | 2 +- .../logging/log_analysis_setup/setup_page.tsx | 2 +- .../setup_status_unknown_prompt.tsx | 2 +- .../subscription_splash_content.tsx | 2 +- .../logging/log_customization_menu.tsx | 2 +- .../log_entry_examples/log_entry_examples.tsx | 2 +- .../logging/log_highlights_menu.tsx | 2 +- .../logging/log_minimap/density_chart.tsx | 2 +- .../log_minimap/highlighted_interval.tsx | 2 +- .../logging/log_minimap/log_minimap.tsx | 2 +- .../logging/log_minimap/search_marker.tsx | 2 +- .../logging/log_minimap/time_ruler.tsx | 2 +- .../log_search_controls/log_search_input.tsx | 2 +- .../components/logging/log_statusbar.tsx | 2 +- .../log_text_stream/column_headers.tsx | 2 +- .../logging/log_text_stream/field_value.tsx | 2 +- .../logging/log_text_stream/highlighting.tsx | 2 +- .../logging/log_text_stream/jump_to_tail.tsx | 2 +- .../log_text_stream/loading_item_view.tsx | 2 +- .../log_text_stream/log_entry_column.tsx | 2 +- .../log_entry_context_menu.tsx | 2 +- .../log_entry_field_column.test.tsx | 2 +- .../log_entry_field_column.tsx | 2 +- .../log_entry_message_column.test.tsx | 2 +- .../log_entry_message_column.tsx | 2 +- .../logging/log_text_stream/log_entry_row.tsx | 3 +- .../log_entry_timestamp_column.tsx | 2 +- .../scrollable_log_text_stream_view.tsx | 2 +- .../logging/log_text_stream/text_styles.tsx | 2 +- .../log_text_stream/vertical_scroll_panel.tsx | 2 +- .../components/navigation/app_navigation.tsx | 2 +- .../components/navigation/routed_tabs.tsx | 2 +- .../plugins/infra/public/components/page.tsx | 2 +- .../infra/public/components/toolbar_panel.ts | 2 +- x-pack/plugins/infra/public/pages/error.tsx | 2 +- .../page_results_content.tsx | 3 +- .../top_categories/category_expression.tsx | 2 +- .../sections/top_categories/datasets_list.tsx | 2 +- .../single_metric_comparison.tsx | 2 +- .../top_categories/top_categories_table.tsx | 2 +- .../log_entry_rate/page_results_content.tsx | 3 +- .../sections/anomalies/expanded_row.tsx | 2 +- .../sections/anomalies/index.tsx | 2 +- .../sections/anomalies/log_entry_example.tsx | 2 +- .../logs/settings/add_log_column_popover.tsx | 2 +- .../pages/logs/stream/page_logs_content.tsx | 2 +- .../logs/stream/page_view_log_in_context.tsx | 2 +- .../components/bottom_drawer.tsx | 4 +- .../components/dropdown_button.tsx | 2 +- .../inventory_view/components/layout.tsx | 2 +- .../subscription_splash_content.tsx | 2 +- .../components/node_details/overlay.tsx | 2 +- .../tabs/processes/process_row.tsx | 2 +- .../tabs/processes/process_row_charts.tsx | 2 +- .../tabs/processes/processes_table.tsx | 2 +- .../tabs/processes/summary_table.tsx | 2 +- .../node_details/tabs/properties/index.tsx | 2 +- .../components/node_details/tabs/shared.tsx | 2 +- .../components/nodes_overview.tsx | 2 +- .../components/timeline/timeline.tsx | 2 +- .../waffle/conditional_tooltip.test.tsx | 2 +- .../components/waffle/conditional_tooltip.tsx | 2 +- .../components/waffle/gradient_legend.tsx | 2 +- .../components/waffle/group_name.tsx | 2 +- .../components/waffle/group_of_groups.tsx | 2 +- .../components/waffle/group_of_nodes.tsx | 2 +- .../components/waffle/legend.tsx | 2 +- .../components/waffle/legend_controls.tsx | 2 +- .../inventory_view/components/waffle/map.tsx | 2 +- .../metric_control/custom_metric_form.tsx | 2 +- .../metric_control/metrics_edit_mode.tsx | 2 +- .../waffle/metric_control/mode_switcher.tsx | 2 +- .../inventory_view/components/waffle/node.tsx | 2 +- .../components/waffle/node_context_menu.tsx | 3 +- .../components/waffle/palette_preview.tsx | 2 +- .../waffle/stepped_gradient_legend.tsx | 2 +- .../components/waffle/steps_legend.tsx | 2 +- .../waffle/waffle_group_by_controls.tsx | 2 +- .../waffle/waffle_sort_controls.tsx | 2 +- .../waffle/waffle_time_controls.tsx | 2 +- .../components/gauges_section_vis.tsx | 2 +- .../metric_detail/components/invalid_node.tsx | 2 +- .../components/layout_content.tsx | 2 +- .../components/metadata_details.tsx | 2 +- .../components/node_details_page.tsx | 2 +- .../metric_detail/components/side_nav.tsx | 2 +- .../components/time_controls.tsx | 2 +- .../pages/metrics/metric_detail/index.tsx | 6 ++- .../pages/metrics/metric_detail/types.ts | 2 +- .../metrics_explorer/components/chart.tsx | 2 +- .../public/application/index.tsx | 2 +- .../__stories__/core_vitals.stories.tsx | 2 +- .../observability/public/hooks/use_theme.tsx | 2 +- .../pages/overview/overview.stories.tsx | 2 +- .../public/typings/eui_styled_components.tsx | 47 ------------------- .../observability/public/typings/index.ts | 1 - .../public/utils/test_helper.tsx | 2 +- .../mock/endpoint/app_root_provider.tsx | 2 +- .../pages/policy/view/vertical_divider.ts | 2 +- .../plugins/uptime/public/apps/uptime_app.tsx | 2 +- .../ping_list/columns/ping_timestamp.tsx | 3 +- .../synthetics/waterfall/components/styles.ts | 2 +- .../waterfall/components/waterfall.test.tsx | 2 +- .../kuery_bar/typeahead/suggestion.tsx | 2 +- .../kuery_bar/typeahead/suggestions.tsx | 2 +- .../columns/monitor_status_column.test.tsx | 2 +- .../columns/monitor_status_column.tsx | 2 +- .../monitor_list/monitor_list.test.tsx | 2 +- .../uptime/public/lib/helper/rtl_helpers.tsx | 2 +- x-pack/plugins/xpack_legacy/common/index.ts | 7 --- 170 files changed, 193 insertions(+), 242 deletions(-) rename {x-pack/plugins/xpack_legacy => src/plugins/kibana_react}/common/eui_styled_components.tsx (86%) create mode 100644 src/plugins/kibana_react/common/index.ts delete mode 100644 x-pack/plugins/observability/public/typings/eui_styled_components.tsx delete mode 100644 x-pack/plugins/xpack_legacy/common/index.ts diff --git a/x-pack/plugins/xpack_legacy/common/eui_styled_components.tsx b/src/plugins/kibana_react/common/eui_styled_components.tsx similarity index 86% rename from x-pack/plugins/xpack_legacy/common/eui_styled_components.tsx rename to src/plugins/kibana_react/common/eui_styled_components.tsx index aab16f9d79c4b..fe002500309ad 100644 --- a/x-pack/plugins/xpack_legacy/common/eui_styled_components.tsx +++ b/src/plugins/kibana_react/common/eui_styled_components.tsx @@ -1,7 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. */ import React from 'react'; diff --git a/src/plugins/kibana_react/common/index.ts b/src/plugins/kibana_react/common/index.ts new file mode 100644 index 0000000000000..0af8ed573699b --- /dev/null +++ b/src/plugins/kibana_react/common/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +export * from './eui_styled_components'; diff --git a/src/plugins/kibana_react/kibana.json b/src/plugins/kibana_react/kibana.json index f2f0da53e6280..6bf7ff1d82070 100644 --- a/src/plugins/kibana_react/kibana.json +++ b/src/plugins/kibana_react/kibana.json @@ -2,5 +2,6 @@ "id": "kibanaReact", "version": "kibana", "ui": true, - "server": false + "server": false, + "extraPublicDirs": ["common"] } diff --git a/src/plugins/kibana_react/tsconfig.json b/src/plugins/kibana_react/tsconfig.json index 52899f868dbfb..eb9a24ca141f6 100644 --- a/src/plugins/kibana_react/tsconfig.json +++ b/src/plugins/kibana_react/tsconfig.json @@ -7,11 +7,6 @@ "declaration": true, "declarationMap": true }, - "include": [ - "public/**/*", - "../../../typings/**/*" - ], - "references": [ - { "path": "../kibana_utils/tsconfig.json" } - ] + "include": ["common/**/*", "public/**/*", "../../../typings/**/*"], + "references": [{ "path": "../kibana_utils/tsconfig.json" }] } diff --git a/x-pack/plugins/apm/common/service_health_status.ts b/x-pack/plugins/apm/common/service_health_status.ts index f66e03a9733a3..c2ba42cc20760 100644 --- a/x-pack/plugins/apm/common/service_health_status.ts +++ b/x-pack/plugins/apm/common/service_health_status.ts @@ -5,10 +5,9 @@ */ import { i18n } from '@kbn/i18n'; +import { EuiTheme } from '../../../../src/plugins/kibana_react/common'; import { ANOMALY_SEVERITY } from '../../ml/common'; -import { EuiTheme } from '../../xpack_legacy/common'; - export enum ServiceHealthStatus { healthy = 'healthy', critical = 'critical', diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.stories.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.stories.tsx index ff95d6fd1254c..a7b0b0393ea94 100644 --- a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.stories.tsx @@ -5,7 +5,7 @@ */ import React, { ComponentType } from 'react'; -import { EuiThemeProvider } from '../../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common'; import { Exception } from '../../../../../typings/es_schemas/raw/error_raw'; import { ExceptionStacktrace } from './ExceptionStacktrace'; diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__stories__/MapTooltip.stories.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__stories__/MapTooltip.stories.tsx index 1053dd611d519..50615c61dea1f 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__stories__/MapTooltip.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__stories__/MapTooltip.stories.tsx @@ -6,7 +6,7 @@ import { storiesOf } from '@storybook/react'; import React from 'react'; -import { EuiThemeProvider } from '../../../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../../../src/plugins/kibana_react/common'; import { MapToolTip } from '../MapToolTip'; import { COUNTRY_NAME, TRANSACTION_DURATION_COUNTRY } from '../useLayerList'; diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/utils/test_helper.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/utils/test_helper.tsx index d5b8cd83d437c..ef44746ffd3f5 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/utils/test_helper.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/utils/test_helper.tsx @@ -11,7 +11,7 @@ import { of } from 'rxjs'; import { createMemoryHistory } from 'history'; import { Router } from 'react-router-dom'; import { MemoryHistory } from 'history'; -import { EuiThemeProvider } from '../../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common'; import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public'; import { UrlParamsProvider } from '../../../../context/url_params_context/url_params_context'; diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx index 313b262508c61..b36cb363f4c25 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx @@ -7,7 +7,7 @@ import cytoscape from 'cytoscape'; import { HttpSetup } from 'kibana/public'; import React, { ComponentType } from 'react'; -import { EuiThemeProvider } from '../../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common'; import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context'; import { MockUrlParamsContextProvider } from '../../../../context/url_params_context/mock_url_params_context_provider'; import { createCallApmApi } from '../../../../services/rest/createCallApmApi'; diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/service_stats_list.stories.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/service_stats_list.stories.tsx index 052f9e9515751..4212cdd29e81a 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/service_stats_list.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/service_stats_list.stories.tsx @@ -5,7 +5,7 @@ */ import React, { ComponentType } from 'react'; -import { EuiThemeProvider } from '../../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common'; import { ServiceStatsList } from './ServiceStatsList'; export default { diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/Cytoscape.stories.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/Cytoscape.stories.tsx index a4985d2f5ab0c..e66eeb4759d50 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/Cytoscape.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/Cytoscape.stories.tsx @@ -7,7 +7,7 @@ import { EuiCard, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import cytoscape from 'cytoscape'; import React, { ComponentType } from 'react'; -import { EuiThemeProvider } from '../../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common'; import { Cytoscape } from '../Cytoscape'; import { iconForNode } from '../icons'; import { Centerer } from './centerer'; diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/cytoscape_example_data.stories.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/cytoscape_example_data.stories.tsx index 0673735ba0adb..349d60f481bac 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/cytoscape_example_data.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/cytoscape_example_data.stories.tsx @@ -16,7 +16,7 @@ import { EuiToolTip, } from '@elastic/eui'; import React, { ComponentType, useEffect, useState } from 'react'; -import { EuiThemeProvider } from '../../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common'; import { Cytoscape } from '../Cytoscape'; import { Centerer } from './centerer'; import exampleResponseHipsterStore from './example_response_hipster_store.json'; diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape_options.ts b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape_options.ts index 9a0ebb7173c26..a1e49876d8291 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape_options.ts +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape_options.ts @@ -5,7 +5,7 @@ */ import cytoscape from 'cytoscape'; import { CSSProperties } from 'react'; -import { EuiTheme } from '../../../../../observability/public'; +import { EuiTheme } from '../../../../../../../src/plugins/kibana_react/common'; import { ServiceAnomalyStats } from '../../../../common/anomaly_detection'; import { SERVICE_NAME, diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/index.test.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/index.test.tsx index c6f82e3492750..7980543e41b16 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/index.test.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/index.test.tsx @@ -8,9 +8,9 @@ import { render } from '@testing-library/react'; import { createMemoryHistory } from 'history'; import { CoreStart } from 'kibana/public'; import React, { ReactNode } from 'react'; -import { createKibanaReactContext } from 'src/plugins/kibana_react/public'; +import { createKibanaReactContext } from '../../../../../../../src/plugins/kibana_react/public'; import { License } from '../../../../../licensing/common/license'; -import { EuiThemeProvider } from '../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common'; import { MockApmPluginContextWrapper } from '../../../context/apm_plugin/mock_apm_plugin_context'; import { LicenseContext } from '../../../context/license/license_context'; import * as useFetcherModule from '../../../hooks/use_fetcher'; diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/use_cytoscape_event_handlers.test.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/use_cytoscape_event_handlers.test.tsx index ab16da1410662..767186781aff2 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/use_cytoscape_event_handlers.test.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/use_cytoscape_event_handlers.test.tsx @@ -7,7 +7,8 @@ import { renderHook } from '@testing-library/react-hooks'; import cytoscape from 'cytoscape'; import dagre from 'cytoscape-dagre'; -import { EuiTheme, useUiTracker } from '../../../../../observability/public'; +import { EuiTheme } from '../../../../../../../src/plugins/kibana_react/common'; +import { useUiTracker } from '../../../../../observability/public'; import { useCytoscapeEventHandlers } from './use_cytoscape_event_handlers'; import lodash from 'lodash'; diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/use_cytoscape_event_handlers.ts b/x-pack/plugins/apm/public/components/app/ServiceMap/use_cytoscape_event_handlers.ts index a2b229780ead0..cf7ee50f242b9 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/use_cytoscape_event_handlers.ts +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/use_cytoscape_event_handlers.ts @@ -7,7 +7,8 @@ import cytoscape from 'cytoscape'; import { debounce } from 'lodash'; import { useEffect } from 'react'; -import { EuiTheme, useUiTracker } from '../../../../../observability/public'; +import { EuiTheme } from '../../../../../../../src/plugins/kibana_react/common'; +import { useUiTracker } from '../../../../../observability/public'; import { getAnimationOptions, getNodeHeight } from './cytoscape_options'; /* diff --git a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.stories.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.stories.tsx index 5ca643428e49c..a03b0b255e9f1 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.stories.tsx @@ -13,6 +13,7 @@ import { storiesOf } from '@storybook/react'; import React from 'react'; import { HttpSetup } from 'kibana/public'; +import { EuiThemeProvider } from '../../../../../../../../../src/plugins/kibana_react/common'; import { AgentConfiguration } from '../../../../../../common/agent_configuration/configuration_types'; import { FETCH_STATUS } from '../../../../../hooks/use_fetcher'; import { createCallApmApi } from '../../../../../services/rest/createCallApmApi'; @@ -21,7 +22,6 @@ import { ApmPluginContext, ApmPluginContextValue, } from '../../../../../context/apm_plugin/apm_plugin_context'; -import { EuiThemeProvider } from '../../../../../../../observability/public'; storiesOf( 'app/Settings/AgentConfigurations/AgentConfigurationCreateEdit', diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx index a94c48f02c101..b7806774536fa 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx @@ -23,10 +23,10 @@ import d3 from 'd3'; import { isEmpty } from 'lodash'; import React from 'react'; import { ValuesType } from 'utility-types'; -import { useTheme } from '../../../../../../observability/public'; import { getDurationFormatter } from '../../../../../common/utils/formatters'; import type { IUrlParams } from '../../../../context/url_params_context/types'; import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; +import { useTheme } from '../../../../hooks/use_theme'; import { APIReturnType } from '../../../../services/rest/createCallApmApi'; import { unit } from '../../../../style/variables'; import { ChartContainer } from '../../../shared/charts/chart_container'; diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx index 5217c2abb11dc..6e78d30bfa1d9 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx @@ -6,7 +6,7 @@ import React, { ComponentType } from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { EuiThemeProvider } from '../../../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../../../src/plugins/kibana_react/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { TraceAPIResponse } from '../../../../../../server/lib/traces/get_trace'; import { MockApmPluginContextWrapper } from '../../../../../context/apm_plugin/mock_apm_plugin_context'; diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx index eb8068bc8114d..7c2729dbb0a16 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx @@ -9,10 +9,10 @@ import { CoreStart } from 'kibana/public'; import { merge } from 'lodash'; import React, { ReactNode } from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { createKibanaReactContext } from 'src/plugins/kibana_react/public'; +import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common'; +import { createKibanaReactContext } from '../../../../../../../src/plugins/kibana_react/public'; import { ServiceHealthStatus } from '../../../../common/service_health_status'; import { ServiceInventory } from '.'; -import { EuiThemeProvider } from '../../../../../observability/public'; import { ApmPluginContextValue } from '../../../context/apm_plugin/apm_plugin_context'; import { mockApmPluginContextValue, diff --git a/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.test.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.test.tsx index 072b5b5bc7a96..aaaaf5174e4fd 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.test.tsx @@ -8,7 +8,7 @@ import { shallow } from 'enzyme'; import React from 'react'; import { AgentMarker } from './AgentMarker'; import { AgentMark } from '../../../../app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks'; -import { EuiThemeProvider } from '../../../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../../../src/plugins/kibana_react/common'; describe('AgentMarker', () => { const mark = { diff --git a/x-pack/plugins/apm/public/components/shared/time_comparison/index.test.tsx b/x-pack/plugins/apm/public/components/shared/time_comparison/index.test.tsx index 6348097a3e3ad..6790301c13abf 100644 --- a/x-pack/plugins/apm/public/components/shared/time_comparison/index.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/time_comparison/index.test.tsx @@ -6,7 +6,7 @@ import { render } from '@testing-library/react'; import React, { ReactNode } from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { EuiThemeProvider } from '../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common'; import { MockUrlParamsContextProvider } from '../../../context/url_params_context/mock_url_params_context_provider'; import { IUrlParams } from '../../../context/url_params_context/types'; import { diff --git a/x-pack/plugins/apm/public/hooks/use_theme.tsx b/x-pack/plugins/apm/public/hooks/use_theme.tsx index e372a764a9505..3c50424b219dd 100644 --- a/x-pack/plugins/apm/public/hooks/use_theme.tsx +++ b/x-pack/plugins/apm/public/hooks/use_theme.tsx @@ -6,7 +6,7 @@ import { useContext } from 'react'; import { ThemeContext } from 'styled-components'; -import { EuiTheme } from '../../../observability/public'; +import { EuiTheme } from '../../../../../src/plugins/kibana_react/common'; export function useTheme(): EuiTheme { const theme = useContext(ThemeContext); diff --git a/x-pack/plugins/apm/public/selectors/latency_chart_selector.test.ts b/x-pack/plugins/apm/public/selectors/latency_chart_selector.test.ts index 7b0826fa76883..7214d0b99bd6a 100644 --- a/x-pack/plugins/apm/public/selectors/latency_chart_selector.test.ts +++ b/x-pack/plugins/apm/public/selectors/latency_chart_selector.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiTheme } from '../../../xpack_legacy/common'; +import { EuiTheme } from '../../../../../src/plugins/kibana_react/common'; import { LatencyAggregationType } from '../../common/latency_aggregation_types'; import { getLatencyChartSelector, diff --git a/x-pack/plugins/apm/public/selectors/latency_chart_selectors.ts b/x-pack/plugins/apm/public/selectors/latency_chart_selectors.ts index dba698ffb1bc1..8ef57ac74be66 100644 --- a/x-pack/plugins/apm/public/selectors/latency_chart_selectors.ts +++ b/x-pack/plugins/apm/public/selectors/latency_chart_selectors.ts @@ -6,7 +6,7 @@ import { Fit } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; import { rgba } from 'polished'; -import { EuiTheme } from '../../../observability/public'; +import { EuiTheme } from '../../../../../src/plugins/kibana_react/common'; import { asDuration } from '../../common/utils/formatters'; import { APMChartSpec, Coordinate } from '../../typings/timeseries'; import { APIReturnType } from '../services/rest/createCallApmApi'; diff --git a/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.test.ts b/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.test.ts index 03877b9e5bff2..e5fed2ee2ae22 100644 --- a/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.test.ts +++ b/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiTheme } from '../../../observability/public'; +import { EuiTheme } from '../../../../../src/plugins/kibana_react/common'; import { getThrouputChartSelector, ThrouputChartsResponse, diff --git a/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.ts b/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.ts index a392f247aec42..b21fcb99fbe1a 100644 --- a/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.ts +++ b/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.ts @@ -5,7 +5,7 @@ */ import { difference, zipObject } from 'lodash'; -import { EuiTheme } from '../../../observability/public'; +import { EuiTheme } from '../../../../../src/plugins/kibana_react/common'; import { asTransactionRate } from '../../common/utils/formatters'; import { TimeSeries } from '../../typings/timeseries'; import { APIReturnType } from '../services/rest/createCallApmApi'; diff --git a/x-pack/plugins/apm/public/utils/testHelpers.tsx b/x-pack/plugins/apm/public/utils/testHelpers.tsx index 21c87c18be363..98745b4f6c636 100644 --- a/x-pack/plugins/apm/public/utils/testHelpers.tsx +++ b/x-pack/plugins/apm/public/utils/testHelpers.tsx @@ -14,12 +14,12 @@ import moment from 'moment'; import { Moment } from 'moment-timezone'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; +import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common'; import { ESFilter, ESSearchRequest, ESSearchResponse, } from '../../../../typings/elasticsearch'; -import { EuiThemeProvider } from '../../../observability/public'; import { PromiseReturnType } from '../../../observability/typings/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { APMConfig } from '../../server'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/app.tsx b/x-pack/plugins/fleet/public/applications/fleet/app.tsx index ed91c1cb1479c..21372508ee99d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/app.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/app.tsx @@ -36,7 +36,7 @@ import { ProtectedRoute } from './index'; import { FleetConfigType, FleetStartServices } from '../../plugin'; import { UIExtensionsStorage } from './types'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; -import { EuiThemeProvider } from '../../../../xpack_legacy/common'; +import { EuiThemeProvider } from '../../../../../../src/plugins/kibana_react/common'; import { UIExtensionsContext } from './hooks/use_ui_extension'; const ErrorLayout = ({ children }: { children: JSX.Element }) => ( diff --git a/x-pack/plugins/infra/common/inventory_models/aws_ec2/layout.tsx b/x-pack/plugins/infra/common/inventory_models/aws_ec2/layout.tsx index 9494e4aa396a5..348fb8b691b86 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_ec2/layout.tsx +++ b/x-pack/plugins/infra/common/inventory_models/aws_ec2/layout.tsx @@ -15,8 +15,7 @@ import { SubSection } from '../../../public/pages/metrics/metric_detail/componen import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { withTheme } from '../../../../observability/public'; +import { withTheme } from '../../../../../../src/plugins/kibana_react/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { MetadataDetails } from '../../../public/pages/metrics/metric_detail/components/metadata_details'; diff --git a/x-pack/plugins/infra/common/inventory_models/aws_rds/layout.tsx b/x-pack/plugins/infra/common/inventory_models/aws_rds/layout.tsx index 08b865f01b06c..45395eff1f823 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_rds/layout.tsx +++ b/x-pack/plugins/infra/common/inventory_models/aws_rds/layout.tsx @@ -13,8 +13,7 @@ import { Section } from '../../../public/pages/metrics/metric_detail/components/ import { SubSection } from '../../../public/pages/metrics/metric_detail/components/sub_section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { withTheme } from '../../../../observability/public'; +import { withTheme } from '../../../../../../src/plugins/kibana_react/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content'; diff --git a/x-pack/plugins/infra/common/inventory_models/aws_s3/layout.tsx b/x-pack/plugins/infra/common/inventory_models/aws_s3/layout.tsx index e16f8ef6addde..cd78ed56d4bc9 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_s3/layout.tsx +++ b/x-pack/plugins/infra/common/inventory_models/aws_s3/layout.tsx @@ -13,8 +13,7 @@ import { Section } from '../../../public/pages/metrics/metric_detail/components/ import { SubSection } from '../../../public/pages/metrics/metric_detail/components/sub_section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { withTheme } from '../../../../observability/public'; +import { withTheme } from '../../../../../../src/plugins/kibana_react/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content'; diff --git a/x-pack/plugins/infra/common/inventory_models/aws_sqs/layout.tsx b/x-pack/plugins/infra/common/inventory_models/aws_sqs/layout.tsx index ff13f2db104de..7f3b4a6d73bf8 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_sqs/layout.tsx +++ b/x-pack/plugins/infra/common/inventory_models/aws_sqs/layout.tsx @@ -13,8 +13,7 @@ import { Section } from '../../../public/pages/metrics/metric_detail/components/ import { SubSection } from '../../../public/pages/metrics/metric_detail/components/sub_section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { withTheme } from '../../../../observability/public'; +import { withTheme } from '../../../../../../src/plugins/kibana_react/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content'; diff --git a/x-pack/plugins/infra/common/inventory_models/container/layout.tsx b/x-pack/plugins/infra/common/inventory_models/container/layout.tsx index b9366a43e40c6..fc4293e698813 100644 --- a/x-pack/plugins/infra/common/inventory_models/container/layout.tsx +++ b/x-pack/plugins/infra/common/inventory_models/container/layout.tsx @@ -15,8 +15,7 @@ import { SubSection } from '../../../public/pages/metrics/metric_detail/componen import { GaugesSectionVis } from '../../../public/pages/metrics/metric_detail/components/gauges_section_vis'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { withTheme } from '../../../../observability/public'; +import { withTheme } from '../../../../../../src/plugins/kibana_react/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths diff --git a/x-pack/plugins/infra/common/inventory_models/host/layout.tsx b/x-pack/plugins/infra/common/inventory_models/host/layout.tsx index e23118c747a9b..c238c813c8691 100644 --- a/x-pack/plugins/infra/common/inventory_models/host/layout.tsx +++ b/x-pack/plugins/infra/common/inventory_models/host/layout.tsx @@ -5,8 +5,7 @@ */ import React from 'react'; import { i18n } from '@kbn/i18n'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { withTheme } from '../../../../observability/public'; +import { withTheme } from '../../../../../../src/plugins/kibana_react/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { LayoutPropsWithTheme } from '../../../public/pages/metrics/metric_detail/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths diff --git a/x-pack/plugins/infra/common/inventory_models/pod/layout.tsx b/x-pack/plugins/infra/common/inventory_models/pod/layout.tsx index 271e32556ae28..255b9483f70c5 100644 --- a/x-pack/plugins/infra/common/inventory_models/pod/layout.tsx +++ b/x-pack/plugins/infra/common/inventory_models/pod/layout.tsx @@ -15,8 +15,7 @@ import { SubSection } from '../../../public/pages/metrics/metric_detail/componen import { GaugesSectionVis } from '../../../public/pages/metrics/metric_detail/components/gauges_section_vis'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { withTheme } from '../../../../observability/public'; +import { withTheme } from '../../../../../../src/plugins/kibana_react/common'; import * as Nginx from '../shared/layouts/nginx'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { MetadataDetails } from '../../../public/pages/metrics/metric_detail/components/metadata_details'; diff --git a/x-pack/plugins/infra/common/inventory_models/shared/layouts/aws.tsx b/x-pack/plugins/infra/common/inventory_models/shared/layouts/aws.tsx index 6f2791534c17e..f8e20ee6404f6 100644 --- a/x-pack/plugins/infra/common/inventory_models/shared/layouts/aws.tsx +++ b/x-pack/plugins/infra/common/inventory_models/shared/layouts/aws.tsx @@ -15,8 +15,7 @@ import { SubSection } from '../../../../public/pages/metrics/metric_detail/compo import { GaugesSectionVis } from '../../../../public/pages/metrics/metric_detail/components/gauges_section_vis'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ChartSectionVis } from '../../../../public/pages/metrics/metric_detail/components/chart_section_vis'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { withTheme } from '../../../../../observability/public'; +import { withTheme } from '../../../../../../../src/plugins/kibana_react/common'; export const Layout = withTheme(({ metrics, onChangeRangeTime, theme }: LayoutPropsWithTheme) => ( diff --git a/x-pack/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx b/x-pack/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx index cf3a06994cc96..3ec17d96043a5 100644 --- a/x-pack/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx +++ b/x-pack/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx @@ -13,8 +13,7 @@ import { Section } from '../../../../public/pages/metrics/metric_detail/componen import { SubSection } from '../../../../public/pages/metrics/metric_detail/components/sub_section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ChartSectionVis } from '../../../../public/pages/metrics/metric_detail/components/chart_section_vis'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { withTheme } from '../../../../../observability/public'; +import { withTheme } from '../../../../../../../src/plugins/kibana_react/common'; export const Layout = withTheme(({ metrics, onChangeRangeTime, theme }: LayoutPropsWithTheme) => ( diff --git a/x-pack/plugins/infra/public/alerting/inventory/components/expression.tsx b/x-pack/plugins/infra/public/alerting/inventory/components/expression.tsx index 3dc754822879d..f8102d9ead2cb 100644 --- a/x-pack/plugins/infra/public/alerting/inventory/components/expression.tsx +++ b/x-pack/plugins/infra/public/alerting/inventory/components/expression.tsx @@ -30,7 +30,7 @@ import { Comparator, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../server/lib/alerting/metric_threshold/types'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { ThresholdExpression, ForLastExpression, diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx index 1487557bde3a0..cdab53c92d32c 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx @@ -14,7 +14,7 @@ import { ThresholdExpression, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../../triggers_actions_ui/public/common'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { IErrorObject } from '../../../../../triggers_actions_ui/public/types'; import { MetricExpression, AGGREGATION_TYPES } from '../types'; diff --git a/x-pack/plugins/infra/public/apps/common_providers.tsx b/x-pack/plugins/infra/public/apps/common_providers.tsx index 41aa2fd898585..ebfa412410dd7 100644 --- a/x-pack/plugins/infra/public/apps/common_providers.tsx +++ b/x-pack/plugins/infra/public/apps/common_providers.tsx @@ -11,7 +11,7 @@ import { useUiSetting$, KibanaContextProvider, } from '../../../../../src/plugins/kibana_react/public'; -import { EuiThemeProvider } from '../../../observability/public'; +import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common'; import { TriggersAndActionsUIPublicPluginStart } from '../../../triggers_actions_ui/public'; import { createKibanaContextForPlugin } from '../hooks/use_kibana'; import { InfraClientStartDeps } from '../types'; diff --git a/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx b/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx index 08594c5ddba18..757a1a1e80ef3 100644 --- a/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx +++ b/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx @@ -7,7 +7,7 @@ import { EuiFieldSearch, EuiOutsideClickDetector, EuiPanel } from '@elastic/eui'; import React from 'react'; import { QuerySuggestion } from '../../../../../../src/plugins/data/public'; -import { euiStyled } from '../../../../observability/public'; +import { euiStyled } from '../../../../../../src/plugins/kibana_react/common'; import { composeStateUpdaters } from '../../utils/typed_react'; import { SuggestionItem } from './suggestion_item'; diff --git a/x-pack/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx b/x-pack/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx index 4bcb7a7ec8a02..78b36d4e047e6 100644 --- a/x-pack/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx +++ b/x-pack/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx @@ -7,7 +7,7 @@ import { EuiIcon } from '@elastic/eui'; import { transparentize } from 'polished'; import React from 'react'; -import { euiStyled } from '../../../../observability/public'; +import { euiStyled } from '../../../../../../src/plugins/kibana_react/common'; import { QuerySuggestion, QuerySuggestionTypes } from '../../../../../../src/plugins/data/public'; interface Props { diff --git a/x-pack/plugins/infra/public/components/centered_flyout_body.tsx b/x-pack/plugins/infra/public/components/centered_flyout_body.tsx index ec762610f36c4..83d1d4de2b2fa 100644 --- a/x-pack/plugins/infra/public/components/centered_flyout_body.tsx +++ b/x-pack/plugins/infra/public/components/centered_flyout_body.tsx @@ -5,7 +5,7 @@ */ import { EuiFlyoutBody } from '@elastic/eui'; -import { euiStyled } from '../../../observability/public'; +import { euiStyled } from '../../../../../src/plugins/kibana_react/common'; export const CenteredEuiFlyoutBody = euiStyled(EuiFlyoutBody)` & .euiFlyoutBody__overflow { diff --git a/x-pack/plugins/infra/public/components/data_search_error_callout.stories.tsx b/x-pack/plugins/infra/public/components/data_search_error_callout.stories.tsx index 4e46e5fdd3f45..32f9d86c1b904 100644 --- a/x-pack/plugins/infra/public/components/data_search_error_callout.stories.tsx +++ b/x-pack/plugins/infra/public/components/data_search_error_callout.stories.tsx @@ -7,7 +7,7 @@ import { PropsOf } from '@elastic/eui'; import { Meta, Story } from '@storybook/react/types-6-0'; import React from 'react'; -import { EuiThemeProvider } from '../../../observability/public'; +import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common'; import { DataSearchErrorCallout } from './data_search_error_callout'; export default { diff --git a/x-pack/plugins/infra/public/components/data_search_progress.stories.tsx b/x-pack/plugins/infra/public/components/data_search_progress.stories.tsx index d5293a7282305..492aa865dfb61 100644 --- a/x-pack/plugins/infra/public/components/data_search_progress.stories.tsx +++ b/x-pack/plugins/infra/public/components/data_search_progress.stories.tsx @@ -7,7 +7,7 @@ import { PropsOf } from '@elastic/eui'; import { Meta, Story } from '@storybook/react/types-6-0'; import React from 'react'; -import { EuiThemeProvider } from '../../../observability/public'; +import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common'; import { DataSearchProgress } from './data_search_progress'; export default { diff --git a/x-pack/plugins/infra/public/components/empty_states/no_data.tsx b/x-pack/plugins/infra/public/components/empty_states/no_data.tsx index 97dc7ac1f8520..e4a9d82fe8c36 100644 --- a/x-pack/plugins/infra/public/components/empty_states/no_data.tsx +++ b/x-pack/plugins/infra/public/components/empty_states/no_data.tsx @@ -7,7 +7,7 @@ import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; import React from 'react'; -import { euiStyled } from '../../../../observability/public'; +import { euiStyled } from '../../../../../../src/plugins/kibana_react/common'; interface NoDataProps { titleText: string; diff --git a/x-pack/plugins/infra/public/components/empty_states/no_indices.tsx b/x-pack/plugins/infra/public/components/empty_states/no_indices.tsx index b453a4f0bc342..1e73c3abe338d 100644 --- a/x-pack/plugins/infra/public/components/empty_states/no_indices.tsx +++ b/x-pack/plugins/infra/public/components/empty_states/no_indices.tsx @@ -7,7 +7,7 @@ import { EuiEmptyPrompt } from '@elastic/eui'; import React from 'react'; -import { euiStyled } from '../../../../observability/public'; +import { euiStyled } from '../../../../../../src/plugins/kibana_react/common'; interface NoIndicesProps { message: string; diff --git a/x-pack/plugins/infra/public/components/error_page.tsx b/x-pack/plugins/infra/public/components/error_page.tsx index 5c8b576c161d7..c800c75a77120 100644 --- a/x-pack/plugins/infra/public/components/error_page.tsx +++ b/x-pack/plugins/infra/public/components/error_page.tsx @@ -15,7 +15,7 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import { euiStyled } from '../../../observability/public'; +import { euiStyled } from '../../../../../src/plugins/kibana_react/common'; import { FlexPage } from './page'; interface Props { diff --git a/x-pack/plugins/infra/public/components/eui/toolbar/toolbar.tsx b/x-pack/plugins/infra/public/components/eui/toolbar/toolbar.tsx index 912550b90b9b9..9ac1996f94b97 100644 --- a/x-pack/plugins/infra/public/components/eui/toolbar/toolbar.tsx +++ b/x-pack/plugins/infra/public/components/eui/toolbar/toolbar.tsx @@ -6,7 +6,7 @@ import { EuiPanel } from '@elastic/eui'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; export const Toolbar = euiStyled(EuiPanel).attrs(() => ({ grow: false, diff --git a/x-pack/plugins/infra/public/components/fixed_datepicker.tsx b/x-pack/plugins/infra/public/components/fixed_datepicker.tsx index fcebb89561826..f4b34b65711cb 100644 --- a/x-pack/plugins/infra/public/components/fixed_datepicker.tsx +++ b/x-pack/plugins/infra/public/components/fixed_datepicker.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { EuiDatePicker, EuiDatePickerProps } from '@elastic/eui'; -import { euiStyled } from '../../../observability/public'; +import { euiStyled } from '../../../../../src/plugins/kibana_react/common'; export const FixedDatePicker = euiStyled( ({ diff --git a/x-pack/plugins/infra/public/components/loading/index.tsx b/x-pack/plugins/infra/public/components/loading/index.tsx index 8c25dfc2380f9..e6a7aa88391f3 100644 --- a/x-pack/plugins/infra/public/components/loading/index.tsx +++ b/x-pack/plugins/infra/public/components/loading/index.tsx @@ -7,7 +7,7 @@ import { EuiLoadingChart, EuiPanel, EuiText } from '@elastic/eui'; import * as React from 'react'; -import { euiStyled } from '../../../../observability/public'; +import { euiStyled } from '../../../../../../src/plugins/kibana_react/common'; interface InfraLoadingProps { text: string | JSX.Element; diff --git a/x-pack/plugins/infra/public/components/loading_overlay_wrapper.tsx b/x-pack/plugins/infra/public/components/loading_overlay_wrapper.tsx index 3b22ee24cee07..b849e24167697 100644 --- a/x-pack/plugins/infra/public/components/loading_overlay_wrapper.tsx +++ b/x-pack/plugins/infra/public/components/loading_overlay_wrapper.tsx @@ -8,7 +8,7 @@ import { EuiLoadingSpinner } from '@elastic/eui'; import { transparentize } from 'polished'; import React from 'react'; -import { euiStyled } from '../../../observability/public'; +import { euiStyled } from '../../../../../src/plugins/kibana_react/common'; export const LoadingOverlayWrapper: React.FC< React.HTMLAttributes & { diff --git a/x-pack/plugins/infra/public/components/log_stream/index.tsx b/x-pack/plugins/infra/public/components/log_stream/index.tsx index 3d69b6a022987..b485a21221af2 100644 --- a/x-pack/plugins/infra/public/components/log_stream/index.tsx +++ b/x-pack/plugins/infra/public/components/log_stream/index.tsx @@ -6,7 +6,7 @@ import React, { useMemo, useCallback, useEffect } from 'react'; import { noop } from 'lodash'; -import { euiStyled } from '../../../../observability/public'; +import { euiStyled } from '../../../../../../src/plugins/kibana_react/common'; import { LogEntryCursor } from '../../../common/log_entry'; diff --git a/x-pack/plugins/infra/public/components/log_stream/log_stream.stories.mdx b/x-pack/plugins/infra/public/components/log_stream/log_stream.stories.mdx index d579432c6291f..bda52d9323eb6 100644 --- a/x-pack/plugins/infra/public/components/log_stream/log_stream.stories.mdx +++ b/x-pack/plugins/infra/public/components/log_stream/log_stream.stories.mdx @@ -2,7 +2,7 @@ import { Meta, Story, Canvas, ArgsTable } from '@storybook/addon-docs/blocks'; import { Subject } from 'rxjs'; import { I18nProvider } from '@kbn/i18n/react'; -import { EuiThemeProvider } from '../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../src/plugins/kibana_react/common'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; import { DEFAULT_SOURCE_CONFIGURATION } from '../../test_utils/source_configuration'; diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.stories.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.stories.tsx index 7caf75417091a..d6c993b28514b 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.stories.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.stories.tsx @@ -7,7 +7,7 @@ import { action } from '@storybook/addon-actions'; import { storiesOf } from '@storybook/react'; import React from 'react'; -import { EuiThemeProvider } from '../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common'; import { QualityWarning } from '../../../../common/log_analysis'; import { CategoryQualityWarnings } from './quality_warning_notices'; diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.tsx index 4bf618923a138..6503dc6110591 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.tsx @@ -16,7 +16,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { groupBy } from 'lodash'; import React, { Fragment, useState } from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { CategoryQualityWarning, CategoryQualityWarningReason, diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.stories.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.stories.tsx index 79b3b9ce9bc6f..932b499430aa9 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.stories.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.stories.tsx @@ -7,7 +7,7 @@ import { actions } from '@storybook/addon-actions'; import { storiesOf } from '@storybook/react'; import React from 'react'; -import { EuiThemeProvider } from '../../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common'; import { InitialConfigurationStep } from './initial_configuration_step'; storiesOf('infra/logAnalysis/SetupInitialConfigurationStep', module) diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_results_privileges_prompt.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_results_privileges_prompt.tsx index 3aa8b544b7b54..04414d43bfde4 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_results_privileges_prompt.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_results_privileges_prompt.tsx @@ -6,7 +6,7 @@ import { EuiEmptyPrompt } from '@elastic/eui'; import React from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { missingMlPrivilegesTitle, missingMlResultsPrivilegesDescription, diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_setup_privileges_prompt.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_setup_privileges_prompt.tsx index 6a5a1da890418..2535192681b8a 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_setup_privileges_prompt.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_setup_privileges_prompt.tsx @@ -6,7 +6,7 @@ import { EuiEmptyPrompt } from '@elastic/eui'; import React from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { missingMlPrivilegesTitle, missingMlSetupPrivilegesDescription, diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx index 4b15ce19ef096..f711e5100391d 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; export const MlUnavailablePrompt: React.FunctionComponent<{}> = () => ( = ({ children, diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx index 334aaa251f524..615c78080c7ae 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiEmptyPrompt, EuiButton } from '@elastic/eui'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; interface Props { retry: () => void; diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/subscription_splash_content.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/subscription_splash_content.tsx index 366d31897fe06..660f416e3b7b4 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/subscription_splash_content.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/subscription_splash_content.tsx @@ -24,7 +24,7 @@ import { HttpStart } from 'src/core/public'; import { LoadingPage } from '../../loading_page'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { useTrialStatus } from '../../../hooks/use_trial_status'; export const SubscriptionSplashContent: React.FC = () => { diff --git a/x-pack/plugins/infra/public/components/logging/log_customization_menu.tsx b/x-pack/plugins/infra/public/components/logging/log_customization_menu.tsx index 84074568bcfef..5433c435d0647 100644 --- a/x-pack/plugins/infra/public/components/logging/log_customization_menu.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_customization_menu.tsx @@ -8,7 +8,7 @@ import { EuiButtonEmpty, EuiPopover } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import * as React from 'react'; -import { euiStyled } from '../../../../observability/public'; +import { euiStyled } from '../../../../../../src/plugins/kibana_react/common'; interface LogCustomizationMenuState { isShown: boolean; diff --git a/x-pack/plugins/infra/public/components/logging/log_entry_examples/log_entry_examples.tsx b/x-pack/plugins/infra/public/components/logging/log_entry_examples/log_entry_examples.tsx index 2d15068e51da5..e8ffd408087d0 100644 --- a/x-pack/plugins/infra/public/components/logging/log_entry_examples/log_entry_examples.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_entry_examples/log_entry_examples.tsx @@ -6,7 +6,7 @@ import React from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { LogEntryExampleMessagesEmptyIndicator } from './log_entry_examples_empty_indicator'; import { LogEntryExampleMessagesFailureIndicator } from './log_entry_examples_failure_indicator'; import { LogEntryExampleMessagesLoadingIndicator } from './log_entry_examples_loading_indicator'; diff --git a/x-pack/plugins/infra/public/components/logging/log_highlights_menu.tsx b/x-pack/plugins/infra/public/components/logging/log_highlights_menu.tsx index 7beead461cb2e..00e9c412ddd3e 100644 --- a/x-pack/plugins/infra/public/components/logging/log_highlights_menu.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_highlights_menu.tsx @@ -18,7 +18,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { debounce } from 'lodash'; import React, { useCallback, useMemo, useState } from 'react'; -import { euiStyled } from '../../../../observability/public'; +import { euiStyled } from '../../../../../../src/plugins/kibana_react/common'; import { useVisibilityState } from '../../utils/use_visibility_state'; interface LogHighlightsMenuProps { diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx index 0528d59f0b3d5..311e25e7c6639 100644 --- a/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx @@ -9,7 +9,7 @@ import { area, curveMonotoneY } from 'd3-shape'; import { max } from 'lodash'; import * as React from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { LogEntriesSummaryBucket } from '../../../../common/http_api'; interface DensityChartProps { diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx index 2869f8d0087c2..53d9985c20dc1 100644 --- a/x-pack/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx @@ -6,7 +6,7 @@ import * as React from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; interface HighlightedIntervalProps { className?: string; diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx index 496d4ebf924a3..a53ce9e3fabc6 100644 --- a/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx @@ -7,7 +7,7 @@ import { scaleLinear } from 'd3-scale'; import * as React from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { LogEntryTime } from '../../../../common/log_entry'; import { DensityChart } from './density_chart'; import { HighlightedInterval } from './highlighted_interval'; diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/search_marker.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/search_marker.tsx index 6271627589394..6c63654eef804 100644 --- a/x-pack/plugins/infra/public/components/logging/log_minimap/search_marker.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_minimap/search_marker.tsx @@ -7,7 +7,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import * as React from 'react'; -import { euiStyled, keyframes } from '../../../../../observability/public'; +import { euiStyled, keyframes } from '../../../../../../../src/plugins/kibana_react/common'; import { LogEntryTime } from '../../../../common/log_entry'; import { SearchMarkerTooltip } from './search_marker_tooltip'; import { LogEntriesSummaryHighlightsBucket } from '../../../../common/http_api'; diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx index 458fdbfcd4916..8424e798128e2 100644 --- a/x-pack/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx @@ -7,7 +7,7 @@ import { scaleTime } from 'd3-scale'; import * as React from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { getTimeLabelFormat } from './time_label_formatter'; interface TimeRulerProps { diff --git a/x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_input.tsx b/x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_input.tsx index 248dce8f6bf8c..4c5237d1c9030 100644 --- a/x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_input.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_input.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import classNames from 'classnames'; import * as React from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; interface LogSearchInputProps { className?: string; diff --git a/x-pack/plugins/infra/public/components/logging/log_statusbar.tsx b/x-pack/plugins/infra/public/components/logging/log_statusbar.tsx index 64dda6ce74d89..f0d062270f01b 100644 --- a/x-pack/plugins/infra/public/components/logging/log_statusbar.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_statusbar.tsx @@ -6,7 +6,7 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { euiStyled } from '../../../../observability/public'; +import { euiStyled } from '../../../../../../src/plugins/kibana_react/common'; export const LogStatusbar = euiStyled(EuiFlexGroup).attrs(() => ({ alignItems: 'center', diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx index a38df73837416..23c3e9c4391e4 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import React, { useContext } from 'react'; import { transparentize } from 'polished'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { LogEntryColumn, LogEntryColumnContent, diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx index b13e3569c1e5b..884e26359ba45 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx @@ -6,7 +6,7 @@ import stringify from 'json-stable-stringify'; import React from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { JsonArray, JsonValue } from '../../../../../../../src/plugins/kibana_utils/common'; import { ActiveHighlightMarker, highlightFieldValue, HighlightMarker } from './highlighting'; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx index 2af99e30ce44f..159ed6d427926 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx @@ -6,7 +6,7 @@ import React from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { chooseLightOrDarkColor, tintOrShade } from '../../../utils/styles'; export const ActiveHighlightMarker = euiStyled.mark` diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx index 50c26784bbdab..17b11205e806d 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx @@ -8,7 +8,7 @@ import { EuiButtonEmpty, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import * as React from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; interface LogTextStreamJumpToTailProps { onClickJump?: () => void; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index 1dd6e0b23e6bc..f176bad9e7a17 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -16,7 +16,7 @@ import { FormattedMessage, FormattedTime, FormattedRelative } from '@kbn/i18n/re import * as React from 'react'; import { Unit } from '@elastic/datemath'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { LogTextSeparator } from './log_text_separator'; import { extendDatemath } from '../../../utils/datemath'; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx index a82c2869baa93..d0f2fa7be6f96 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx @@ -6,7 +6,7 @@ import { useMemo } from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { TextScale } from '../../../../common/log_text_scale'; import { LogColumnRenderConfiguration, diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx index 8582be008a44a..d2cb3baef350d 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx @@ -14,7 +14,7 @@ import { EuiContextMenuItem, } from '@elastic/eui'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { LogEntryColumnContent } from './log_entry_column'; interface LogEntryContextMenuItem { diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx index 5813f08497a74..8de9e565b00be 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx @@ -6,7 +6,7 @@ import { render } from '@testing-library/react'; import React from 'react'; -import { EuiThemeProvider } from '../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common'; import { LogFieldColumn } from '../../../../common/http_api'; import { LogEntryFieldColumn } from './log_entry_field_column'; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx index fe5e7f305f60c..4a9b0d0906a76 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { JsonValue } from '../../../../../../../src/plugins/kibana_utils/common'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { LogColumn } from '../../../../common/http_api'; import { isFieldColumn, isHighlightFieldColumn } from '../../../utils/log_entry'; import { FieldValue } from './field_value'; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.test.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.test.tsx index b9871cc3b36f4..5d36e5cd47c59 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.test.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.test.tsx @@ -6,7 +6,7 @@ import { render } from '@testing-library/react'; import React from 'react'; -import { EuiThemeProvider } from '../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common'; import { LogMessageColumn } from '../../../../common/http_api'; import { LogEntryMessageColumn } from './log_entry_message_column'; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx index 65e7b2bf2273d..bfc160ada2e6a 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx @@ -5,7 +5,7 @@ */ import React, { memo, useMemo } from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { LogColumn, LogMessagePart } from '../../../../common/http_api'; import { isConstantSegment, diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx index 5a653300217d7..93c657fbdda97 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx @@ -8,7 +8,8 @@ import React, { memo, useState, useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { isEmpty } from 'lodash'; -import { euiStyled, useUiTracker } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; +import { useUiTracker } from '../../../../../observability/public'; import { isTimestampColumn } from '../../../utils/log_entry'; import { TextScale } from '../../../../common/log_text_scale'; import { LogEntryColumn, LogEntryColumnWidths, iconColumnId } from './log_entry_column'; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx index 21568974463eb..d13fb4b6d7bb6 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx @@ -6,7 +6,7 @@ import React, { memo } from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { TimeFormat, useFormattedTime } from '../../formatted_time'; import { LogEntryColumnContent } from './log_entry_column'; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx index 3c86ce3e32526..d399e47a73562 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx @@ -9,7 +9,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React, { Fragment } from 'react'; import moment from 'moment'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { TextScale } from '../../../../common/log_text_scale'; import { TimeKey, UniqueTimeKey } from '../../../../common/time'; import { callWithoutRepeats } from '../../../utils/handlers'; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx index 7fc97f949f068..d7b94d4c02912 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx @@ -6,7 +6,7 @@ import React, { useMemo, useState, useCallback } from 'react'; -import { euiStyled, css } from '../../../../../observability/public'; +import { euiStyled, css } from '../../../../../../../src/plugins/kibana_react/common'; import { TextScale } from '../../../../common/log_text_scale'; export type WrapMode = 'none' | 'pre-wrapped' | 'long'; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx index 9b8fecc7fb111..a4fed85a6a88e 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx @@ -8,7 +8,7 @@ import { bisector } from 'd3-array'; import { sortBy, throttle } from 'lodash'; import * as React from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { Rect } from './measurable_item_view'; interface VerticalScrollPanelProps { diff --git a/x-pack/plugins/infra/public/components/navigation/app_navigation.tsx b/x-pack/plugins/infra/public/components/navigation/app_navigation.tsx index 9da892ec92ec1..68d523f5066a1 100644 --- a/x-pack/plugins/infra/public/components/navigation/app_navigation.tsx +++ b/x-pack/plugins/infra/public/components/navigation/app_navigation.tsx @@ -6,7 +6,7 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React from 'react'; -import { euiStyled } from '../../../../observability/public'; +import { euiStyled } from '../../../../../../src/plugins/kibana_react/common'; interface AppNavigationProps { 'aria-label': string; diff --git a/x-pack/plugins/infra/public/components/navigation/routed_tabs.tsx b/x-pack/plugins/infra/public/components/navigation/routed_tabs.tsx index d9340d804fb24..1db4e0ee70883 100644 --- a/x-pack/plugins/infra/public/components/navigation/routed_tabs.tsx +++ b/x-pack/plugins/infra/public/components/navigation/routed_tabs.tsx @@ -8,7 +8,7 @@ import { EuiLink, EuiTab, EuiTabs } from '@elastic/eui'; import React from 'react'; import { Route } from 'react-router-dom'; -import { euiStyled } from '../../../../observability/public'; +import { euiStyled } from '../../../../../../src/plugins/kibana_react/common'; import { useLinkProps } from '../../hooks/use_link_props'; import { LinkDescriptor } from '../../hooks/use_link_props'; diff --git a/x-pack/plugins/infra/public/components/page.tsx b/x-pack/plugins/infra/public/components/page.tsx index 9636a5fc3a631..a76500a1d723f 100644 --- a/x-pack/plugins/infra/public/components/page.tsx +++ b/x-pack/plugins/infra/public/components/page.tsx @@ -6,7 +6,7 @@ import { EuiPage } from '@elastic/eui'; -import { euiStyled } from '../../../observability/public'; +import { euiStyled } from '../../../../../src/plugins/kibana_react/common'; export const ColumnarPage = euiStyled.div` display: flex; diff --git a/x-pack/plugins/infra/public/components/toolbar_panel.ts b/x-pack/plugins/infra/public/components/toolbar_panel.ts index 686b563068d60..a0039f6370f1f 100644 --- a/x-pack/plugins/infra/public/components/toolbar_panel.ts +++ b/x-pack/plugins/infra/public/components/toolbar_panel.ts @@ -5,7 +5,7 @@ */ import { EuiPanel } from '@elastic/eui'; -import { euiStyled } from '../../../observability/public'; +import { euiStyled } from '../../../../../src/plugins/kibana_react/common'; export const ToolbarPanel = euiStyled(EuiPanel).attrs(() => ({ grow: false, diff --git a/x-pack/plugins/infra/public/pages/error.tsx b/x-pack/plugins/infra/public/pages/error.tsx index c34af31ce28a7..55892c424f465 100644 --- a/x-pack/plugins/infra/public/pages/error.tsx +++ b/x-pack/plugins/infra/public/pages/error.tsx @@ -15,7 +15,7 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import { euiStyled } from '../../../observability/public'; +import { euiStyled } from '../../../../../src/plugins/kibana_react/common'; import { Header } from '../components/header'; import { ColumnarPage, PageContent } from '../components/page'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx index 5c1e8f2bdcfcc..ecddd8a9aa5be 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx @@ -10,7 +10,8 @@ import { i18n } from '@kbn/i18n'; import moment from 'moment'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { euiStyled, useTrackPageview } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; +import { useTrackPageview } from '../../../../../observability/public'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; import { CategoryJobNoticesSection } from '../../../components/logging/log_analysis_job_status'; import { useLogEntryCategoriesModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_categories'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_expression.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_expression.tsx index be8281ce54556..d5480977e7f9e 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_expression.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_expression.tsx @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import React, { memo } from 'react'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; export const RegularExpressionRepresentation: React.FunctionComponent<{ maximumSegmentCount?: number; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_list.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_list.tsx index 748c82cc5cd5a..779ac3e8c3a07 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_list.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_list.tsx @@ -6,7 +6,7 @@ import React from 'react'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { LogEntryCategoryDataset } from '../../../../../../common/http_api/log_analysis'; import { getFriendlyNameForPartitionId } from '../../../../../../common/log_analysis'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_comparison.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_comparison.tsx index d73f9f33fe5db..e4da367721c2d 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_comparison.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_comparison.tsx @@ -9,7 +9,7 @@ import numeral from '@elastic/numeral'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; export const SingleMetricComparison: React.FunctionComponent<{ currentValue: number; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx index 96abe4ab42669..954b6a9ab3ed3 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import React, { useMemo, useCallback } from 'react'; import useSet from 'react-use/lib/useSet'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { LogEntryCategory, LogEntryCategoryDataset, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx index c4a464a4cffad..09d3746c6ace6 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx @@ -11,7 +11,8 @@ import { stringify } from 'query-string'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { encode, RisonValue } from 'rison-node'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { euiStyled, useTrackPageview } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; +import { useTrackPageview } from '../../../../../observability/public'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; import { bucketSpan } from '../../../../common/log_analysis'; import { TimeKey } from '../../../../common/time'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx index e6489417f2d07..37032a95e9640 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx @@ -9,7 +9,7 @@ import numeral from '@elastic/numeral'; import { i18n } from '@kbn/i18n'; import React from 'react'; import useMount from 'react-use/lib/useMount'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { LogEntryAnomaly } from '../../../../../../common/http_api'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { LogEntryExampleMessages } from '../../../../../components/logging/log_entry_examples/log_entry_examples'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx index ff9b9db2e5087..c89f0329e9f2e 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx @@ -14,7 +14,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { LogEntryRateResults } from '../../use_log_entry_rate_results'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { getAnnotationsForAll, getLogEntryRateCombinedSeries } from '../helpers/data_formatters'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/log_entry_example.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/log_entry_example.tsx index b639cecf676ad..ab3476cd78eb3 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/log_entry_example.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/log_entry_example.tsx @@ -8,7 +8,7 @@ import React, { useMemo, useCallback, useState } from 'react'; import moment from 'moment'; import { encode } from 'rison-node'; import { i18n } from '@kbn/i18n'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { getFriendlyNameForPartitionId } from '../../../../../../common/log_analysis'; import { LogEntryColumn, diff --git a/x-pack/plugins/infra/public/pages/logs/settings/add_log_column_popover.tsx b/x-pack/plugins/infra/public/pages/logs/settings/add_log_column_popover.tsx index 551d200f1895d..887e706c909ba 100644 --- a/x-pack/plugins/infra/public/pages/logs/settings/add_log_column_popover.tsx +++ b/x-pack/plugins/infra/public/pages/logs/settings/add_log_column_popover.tsx @@ -15,7 +15,7 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import React, { useCallback, useMemo } from 'react'; import { v4 as uuidv4 } from 'uuid'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { LogColumnConfiguration } from '../../../utils/source_configuration'; import { useVisibilityState } from '../../../utils/use_visibility_state'; diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx index 05bcd4d0984e8..02125807faf54 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx @@ -5,7 +5,7 @@ */ import React, { useContext, useCallback } from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { AutoSizer } from '../../../components/auto_sizer'; import { LogEntryFlyout } from '../../../components/logging/log_entry_flyout'; import { LogMinimap } from '../../../components/logging/log_minimap'; diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx index 8a4081288b283..3fa89da5b5e51 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx @@ -19,7 +19,7 @@ import React, { useCallback, useContext, useMemo } from 'react'; import { LogEntry } from '../../../../common/http_api'; import { ViewLogInContext } from '../../../containers/logs/view_log_in_context'; import { useViewportDimensions } from '../../../utils/use_viewport_dimensions'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { LogStream } from '../../../components/log_stream'; const MODAL_MARGIN = 25; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx index 5c6e124914f39..6055b60719a68 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx @@ -7,8 +7,8 @@ import React, { useCallback, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiSpacer } from '@elastic/eui'; - -import { euiStyled, useUiTracker } from '../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; +import { useUiTracker } from '../../../../../../observability/public'; import { InfraFormatter } from '../../../../lib/lib'; import { Timeline } from './timeline/timeline'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/dropdown_button.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/dropdown_button.tsx index 62b25d5a36870..b574347970df6 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/dropdown_button.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/dropdown_button.tsx @@ -6,7 +6,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; import React, { ReactNode } from 'react'; -import { withTheme, EuiTheme } from '../../../../../../observability/public'; +import { withTheme, EuiTheme } from '../../../../../../../../src/plugins/kibana_react/common'; interface Props { 'data-test-subj'?: string; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx index 2f312d9ee64ac..f8b0bbf62d2b5 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx @@ -19,7 +19,7 @@ import { useWaffleFiltersContext } from '../hooks/use_waffle_filters'; import { DEFAULT_LEGEND, useWaffleOptionsContext } from '../hooks/use_waffle_options'; import { useSourceContext } from '../../../../containers/source'; import { InfraFormatterType } from '../../../../lib/lib'; -import { euiStyled } from '../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; import { Toolbar } from './toolbars/toolbar'; import { ViewSwitcher } from './waffle/view_switcher'; import { IntervalLabel } from './waffle/interval_label'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/subscription_splash_content.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/subscription_splash_content.tsx index f07c37f5e7ea2..4e18880f1fd22 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/subscription_splash_content.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/subscription_splash_content.tsx @@ -24,7 +24,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { LoadingPage } from '../../../../../../components/loading_page'; import { useTrialStatus } from '../../../../../../hooks/use_trial_status'; import { useKibana } from '../../../../../../../../../../src/plugins/kibana_react/public'; -import { euiStyled } from '../../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../../src/plugins/kibana_react/common'; import { HttpStart } from '../../../../../../../../../../src/core/public'; export const SubscriptionSplashContent: React.FC = () => { diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx index 661844a627a58..a053e2a72cea0 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx @@ -10,7 +10,7 @@ import React, { useMemo, useState } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; import { EuiOutsideClickDetector } from '@elastic/eui'; import { EuiIcon, EuiButtonIcon } from '@elastic/eui'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../../../../lib/lib'; import { InventoryItemType } from '../../../../../../common/inventory_models/types'; import { MetricsTab } from './tabs/metrics/metrics'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row.tsx index 3f0798c4a1670..bc03f9337813f 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row.tsx @@ -21,7 +21,7 @@ import { EuiSpacer, } from '@elastic/eui'; import { AutoSizer } from '../../../../../../../components/auto_sizer'; -import { euiStyled } from '../../../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../../../src/plugins/kibana_react/common'; import { Process } from './types'; import { ProcessRowCharts } from './process_row_charts'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row_charts.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row_charts.tsx index af515ae75854c..a3f75402df2ca 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row_charts.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row_charts.tsx @@ -25,7 +25,7 @@ import { MetricsExplorerChartType } from '../../../../../metrics_explorer/hooks/ import { MetricExplorerSeriesChart } from '../../../../../metrics_explorer/components/series_chart'; import { MetricsExplorerAggregation } from '../../../../../../../../common/http_api'; import { Color } from '../../../../../../../../common/color_palette'; -import { euiStyled } from '../../../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../../../src/plugins/kibana_react/common'; import { useProcessListRowChart } from '../../../../hooks/use_process_list_row_chart'; import { Process } from './types'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/processes_table.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/processes_table.tsx index 1ea6e397e7768..f420fd09e6e68 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/processes_table.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/processes_table.tsx @@ -25,7 +25,7 @@ import { } from '@elastic/eui'; import { ProcessListAPIResponse } from '../../../../../../../../common/http_api'; import { FORMATTERS } from '../../../../../../../../common/formatters'; -import { euiStyled } from '../../../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../../../src/plugins/kibana_react/common'; import { SortBy } from '../../../../hooks/use_process_list'; import { Process } from './types'; import { ProcessRow } from './process_row'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/summary_table.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/summary_table.tsx index 5bbba906b62f2..93d98853539b9 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/summary_table.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/summary_table.tsx @@ -16,7 +16,7 @@ import { EuiDescriptionListDescription, EuiHorizontalRule, } from '@elastic/eui'; -import { euiStyled } from '../../../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../../../src/plugins/kibana_react/common'; import { ProcessListAPIResponse } from '../../../../../../../../common/http_api'; import { STATE_NAMES } from './states'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx index 46b63bc400a2b..f06cdd3cb0875 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx @@ -15,7 +15,7 @@ import { useMetadata } from '../../../../../metric_detail/hooks/use_metadata'; import { getFields } from './build_fields'; import { useWaffleTimeContext } from '../../../../hooks/use_waffle_time'; import { Table } from './table'; -import { euiStyled } from '../../../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../../../src/plugins/kibana_react/common'; import { useWaffleFiltersContext } from '../../../../hooks/use_waffle_filters'; const TabComponent = (props: TabProps) => { diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/shared.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/shared.tsx index 6ff31e86c9d5e..670fc3673de25 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/shared.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/shared.tsx @@ -6,7 +6,7 @@ import { InventoryItemType } from '../../../../../../../common/inventory_models/types'; import { InfraWaffleMapOptions, InfraWaffleMapNode } from '../../../../../../lib/lib'; -import { euiStyled } from '../../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../../src/plugins/kibana_react/common'; export interface TabProps { options: InfraWaffleMapOptions; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/nodes_overview.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/nodes_overview.tsx index 9b6853dcdc751..de31e690e3659 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/nodes_overview.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/nodes_overview.tsx @@ -9,7 +9,7 @@ import React, { useCallback } from 'react'; import { getBreakpoint } from '@elastic/eui'; import { InventoryItemType } from '../../../../../common/inventory_models/types'; -import { euiStyled } from '../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; import { InfraWaffleMapBounds, InfraWaffleMapOptions, InfraFormatter } from '../../../../lib/lib'; import { NoData } from '../../../../components/empty_states'; import { InfraLoadingPanel } from '../../../../components/loading'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx index f1e796ef8ba18..44993f688ebc2 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx @@ -38,7 +38,7 @@ import { MetricsExplorerChartType } from '../../../metrics_explorer/hooks/use_me import { getTimelineChartTheme } from '../../../metrics_explorer/components/helpers/get_chart_theme'; import { calculateDomain } from '../../../metrics_explorer/components/helpers/calculate_domain'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { InfraFormatter } from '../../../../../lib/lib'; import { useMetricsHostsAnomaliesResults } from '../../hooks/use_metrics_hosts_anomalies'; import { useMetricsK8sAnomaliesResults } from '../../hooks/use_metrics_k8s_anomalies'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx index fbca85e2d4496..c69bdb7c4379e 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { mount } from 'enzyme'; // import { act } from 'react-dom/test-utils'; -import { EuiThemeProvider } from '../../../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../../../src/plugins/kibana_react/common'; import { EuiToolTip } from '@elastic/eui'; import { ConditionalToolTip } from './conditional_tooltip'; import { diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx index 7ec1ae905a640..a559882987bfa 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx @@ -8,7 +8,7 @@ import { EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { first } from 'lodash'; import { getCustomMetricLabel } from '../../../../../../common/formatters/get_custom_metric_label'; import { SnapshotCustomMetricInput } from '../../../../../../common/http_api'; -import { withTheme, EuiTheme } from '../../../../../../../observability/public'; +import { withTheme, EuiTheme } from '../../../../../../../../../src/plugins/kibana_react/common'; import { useSourceContext } from '../../../../../containers/source'; import { findInventoryModel } from '../../../../../../common/inventory_models'; import { diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/gradient_legend.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/gradient_legend.tsx index 953d94e51aad0..a900c46376ba6 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/gradient_legend.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/gradient_legend.tsx @@ -6,7 +6,7 @@ import React from 'react'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { InfraFormatter, InfraWaffleMapBounds, diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_name.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_name.tsx index 75a023d49d4aa..1e592b846c320 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_name.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_name.tsx @@ -6,7 +6,7 @@ import { EuiLink, EuiToolTip } from '@elastic/eui'; import React from 'react'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { InfraWaffleMapGroup, InfraWaffleMapOptions } from '../../../../../lib/lib'; interface Props { diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_groups.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_groups.tsx index 760dae169d000..6741cd0f851ea 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_groups.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_groups.tsx @@ -6,7 +6,7 @@ import React from 'react'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { InfraWaffleMapBounds, InfraWaffleMapGroupOfGroups, diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx index 5fcee6193b357..056fc4dcb79fe 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx @@ -6,7 +6,7 @@ import React from 'react'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { InfraWaffleMapBounds, InfraWaffleMapGroupOfNodes, diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend.tsx index ea7bb66e689d9..202d263084896 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend.tsx @@ -5,7 +5,7 @@ */ import React, { useCallback } from 'react'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { InfraFormatter, InfraWaffleMapBounds, diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend_controls.tsx index f4da68d9dead7..13fa3c86fa9aa 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend_controls.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend_controls.tsx @@ -25,7 +25,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { SyntheticEvent, useState, useCallback, useEffect } from 'react'; import { first, last } from 'lodash'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { InfraWaffleMapBounds, InventoryColorPalette, PALETTES } from '../../../../../lib/lib'; import { WaffleLegendOptions } from '../../hooks/use_waffle_options'; import { getColorPalette } from '../../lib/get_color_palette'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx index 8023e3bf7da62..147876d3b377d 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { nodesToWaffleMap } from '../../lib/nodes_to_wafflemap'; import { isWaffleMapGroupWithGroups, isWaffleMapGroupWithNodes } from '../../lib/type_guards'; import { InfraWaffleMapBounds, InfraWaffleMapOptions } from '../../../../../lib/lib'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx index 262d94d8f3674..c1c923092726a 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx @@ -27,7 +27,7 @@ import { SNAPSHOT_CUSTOM_AGGREGATIONS, SnapshotCustomAggregationRT, } from '../../../../../../../common/http_api/snapshot_api'; -import { EuiTheme, withTheme } from '../../../../../../../../xpack_legacy/common'; +import { EuiTheme, withTheme } from '../../../../../../../../../../src/plugins/kibana_react/common'; interface SelectedOption { label: string; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_edit_mode.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_edit_mode.tsx index 831a0cde49cfb..2c37049a9a6f5 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_edit_mode.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_edit_mode.tsx @@ -8,7 +8,7 @@ import { EuiFlexItem, EuiFlexGroup, EuiButtonIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { getCustomMetricLabel } from '../../../../../../../common/formatters/get_custom_metric_label'; import { SnapshotCustomMetricInput } from '../../../../../../../common/http_api/snapshot_api'; -import { EuiTheme, withTheme } from '../../../../../../../../xpack_legacy/common'; +import { EuiTheme, withTheme } from '../../../../../../../../../../src/plugins/kibana_react/common'; interface Props { theme: EuiTheme | undefined; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/mode_switcher.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/mode_switcher.tsx index 956241545e8be..c2076dca6d581 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/mode_switcher.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/mode_switcher.tsx @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { CustomMetricMode } from './types'; import { SnapshotCustomMetricInput } from '../../../../../../../common/http_api/snapshot_api'; -import { EuiTheme, withTheme } from '../../../../../../../../xpack_legacy/common'; +import { EuiTheme, withTheme } from '../../../../../../../../../../src/plugins/kibana_react/common'; interface Props { theme: EuiTheme | undefined; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx index 4024f6b505c29..11d1b60034c78 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { first } from 'lodash'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { InfraWaffleMapBounds, InfraWaffleMapNode, diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx index 3179d4aa05268..0d0333ca27c64 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx @@ -16,6 +16,7 @@ import { createUptimeLink } from '../../lib/create_uptime_link'; import { findInventoryModel, findInventoryFields } from '../../../../../../common/inventory_models'; import { useKibana } from '../../../../../../../../../src/plugins/kibana_react/public'; import { InventoryItemType } from '../../../../../../common/inventory_models/types'; +import { withTheme, EuiTheme } from '../../../../../../../../../src/plugins/kibana_react/common'; import { Section, SectionLinkProps, @@ -24,8 +25,6 @@ import { SectionSubtitle, SectionLinks, SectionLink, - withTheme, - EuiTheme, } from '../../../../../../../observability/public'; import { useLinkProps } from '../../../../../hooks/use_link_props'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/palette_preview.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/palette_preview.tsx index 09e323b55a71a..1d8fcb20a261c 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/palette_preview.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/palette_preview.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { InventoryColorPalette } from '../../../../../lib/lib'; import { getColorPalette } from '../../lib/get_color_palette'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/stepped_gradient_legend.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/stepped_gradient_legend.tsx index fadf6b139a3e8..ed34a32012bd2 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/stepped_gradient_legend.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/stepped_gradient_legend.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { InfraWaffleMapBounds, InfraFormatter, diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/steps_legend.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/steps_legend.tsx index 1ef0f2d0c4288..3ef76c6a70733 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/steps_legend.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/steps_legend.tsx @@ -7,7 +7,7 @@ import { darken } from 'polished'; import React from 'react'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { InfraFormatter, InfraWaffleMapRuleOperator, diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx index 8d2f289621b12..349f9a9b25e9b 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { IFieldType } from 'src/plugins/data/public'; import { InfraGroupByOptions } from '../../../../../lib/lib'; import { CustomFieldPanel } from './custom_field_panel'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { InventoryItemType } from '../../../../../../common/inventory_models/types'; import { SnapshotGroupBy } from '../../../../../../common/http_api/snapshot_api'; import { DropdownButton } from '../dropdown_button'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_sort_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_sort_controls.tsx index a45ac0cee72d9..55466049bc71a 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_sort_controls.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_sort_controls.tsx @@ -7,7 +7,7 @@ import React, { useCallback, useMemo, useState, ReactNode } from 'react'; import { EuiSwitch, EuiContextMenuPanelDescriptor, EuiPopover, EuiContextMenu } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { EuiTheme, withTheme } from '../../../../../../../observability/public'; +import { EuiTheme, withTheme } from '../../../../../../../../../src/plugins/kibana_react/common'; import { WaffleSortOption } from '../../hooks/use_waffle_options'; import { DropdownButton } from '../dropdown_button'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_time_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_time_controls.tsx index da044b1cf99ee..01e938b5e6a62 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_time_controls.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_time_controls.tsx @@ -8,7 +8,7 @@ import { EuiButton, EuiDatePicker, EuiFlexGroup, EuiFlexItem } from '@elastic/eu import { FormattedMessage } from '@kbn/i18n/react'; import moment, { Moment } from 'moment'; import React, { useCallback } from 'react'; -import { withTheme, EuiTheme } from '../../../../../../../observability/public'; +import { withTheme, EuiTheme } from '../../../../../../../../../src/plugins/kibana_react/common'; import { useWaffleTimeContext } from '../../hooks/use_waffle_time'; interface Props { diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/gauges_section_vis.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/gauges_section_vis.tsx index 2d06713f7fe1a..be73c8a0b5e1c 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/gauges_section_vis.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/gauges_section_vis.tsx @@ -16,7 +16,7 @@ import { import { get, last, max } from 'lodash'; import React, { ReactText } from 'react'; -import { euiStyled } from '../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; import { createFormatter } from '../../../../../common/formatters'; import { InventoryFormatterType } from '../../../../../common/inventory_models/types'; import { SeriesOverrides, VisSectionProps } from '../types'; diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx index b9b8e83b598f3..e81dedba64043 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx @@ -7,7 +7,7 @@ import { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import { euiStyled } from '../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; import { ViewSourceConfigurationButton } from '../../../../components/source_configuration'; import { useLinkProps } from '../../../../hooks/use_link_props'; diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/layout_content.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/layout_content.tsx index 4620102517549..e33785632555e 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/layout_content.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/layout_content.tsx @@ -5,7 +5,7 @@ */ import { EuiPageContent } from '@elastic/eui'; -import { euiStyled } from '../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; export const LayoutContent = euiStyled(EuiPageContent)` position: relative; diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/metadata_details.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/metadata_details.tsx index 656378fbc0610..953659731407c 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/metadata_details.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/metadata_details.tsx @@ -9,7 +9,7 @@ import { EuiButtonIcon, EuiFlexGrid, EuiFlexItem, EuiTitle, EuiText } from '@ela import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; import { InfraMetadata } from '../../../../../common/http_api'; -import { euiStyled } from '../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; import { MetadataContext } from '../containers/metadata_context'; interface FieldDef { diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx index 0d0bc8c82397e..a30b2c17d3517 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx @@ -21,7 +21,7 @@ import { AutoSizer } from '../../../../components/auto_sizer'; import { MetricsTimeControls } from './time_controls'; import { SideNavContext, NavItem } from '../lib/side_nav_context'; import { PageBody } from './page_body'; -import { euiStyled } from '../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; import { MetricsTimeInput } from '../hooks/use_metrics_time'; import { InfraMetadata } from '../../../../../common/http_api/metadata_api'; import { PageError } from './page_error'; diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/side_nav.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/side_nav.tsx index 1cba3366acbbb..e1fb307d35f5f 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/side_nav.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/side_nav.tsx @@ -6,7 +6,7 @@ import { EuiHideFor, EuiPageSideBar, EuiShowFor, EuiSideNav } from '@elastic/eui'; import React, { useState, useCallback } from 'react'; -import { euiStyled } from '../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; import { NavItem } from '../lib/side_nav_context'; interface Props { diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/time_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/time_controls.tsx index afee0c0498187..ba9e71021b232 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/time_controls.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/time_controls.tsx @@ -7,7 +7,7 @@ import { EuiSuperDatePicker, OnRefreshChangeProps, OnTimeChangeProps } from '@elastic/eui'; import React, { useCallback } from 'react'; import { UI_SETTINGS } from '../../../../../../../../src/plugins/data/public'; -import { euiStyled } from '../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; import { MetricsTimeInput } from '../hooks/use_metrics_time'; import { useKibanaUiSetting } from '../../../../utils/use_kibana_ui_setting'; import { mapKibanaQuickRangesToDatePickerRanges } from '../../../../utils/map_timepicker_quickranges_to_datepicker_ranges'; diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx index 60c8041fb5ef0..4220303d02cf4 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx @@ -5,7 +5,11 @@ */ import { i18n } from '@kbn/i18n'; import React, { useContext, useState } from 'react'; -import { euiStyled, EuiTheme, withTheme } from '../../../../../observability/public'; +import { + euiStyled, + EuiTheme, + withTheme, +} from '../../../../../../../src/plugins/kibana_react/common'; import { DocumentTitle } from '../../../components/document_title'; import { Header } from '../../../components/header'; import { ColumnarPage, PageContent } from '../../../components/page'; diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/types.ts b/x-pack/plugins/infra/public/pages/metrics/metric_detail/types.ts index 3ec57e23a425d..2331d69d460d1 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/types.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/types.ts @@ -5,7 +5,7 @@ */ import rt from 'io-ts'; -import { EuiTheme } from '../../../../../observability/public'; +import { EuiTheme } from '../../../../../../../src/plugins/kibana_react/common'; import { InventoryFormatterTypeRT } from '../../../../common/inventory_models/types'; import { MetricsTimeInput } from './hooks/use_metrics_time'; import { NodeDetailsMetricData } from '../../../../common/http_api/node_details_api'; diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart.tsx index 41d8014b4a5c1..c228f09cbb645 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart.tsx @@ -25,7 +25,7 @@ import { MetricsExplorerYAxisMode, MetricsExplorerChartOptions, } from '../hooks/use_metrics_explorer_options'; -import { euiStyled } from '../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; import { createFormatterForMetric } from './helpers/create_formatter_for_metric'; import { MetricExplorerSeriesChart } from './series_chart'; import { MetricsExplorerChartContextMenu } from './chart_context_menu'; diff --git a/x-pack/plugins/observability/public/application/index.tsx b/x-pack/plugins/observability/public/application/index.tsx index ea84a417c20eb..d4dfd265ecda6 100644 --- a/x-pack/plugins/observability/public/application/index.tsx +++ b/x-pack/plugins/observability/public/application/index.tsx @@ -7,12 +7,12 @@ import { i18n } from '@kbn/i18n'; import React, { useEffect } from 'react'; import ReactDOM from 'react-dom'; import { Route, Router, Switch } from 'react-router-dom'; +import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common'; import { AppMountParameters, CoreStart } from '../../../../../src/core/public'; import { KibanaContextProvider, RedirectAppLinks, } from '../../../../../src/plugins/kibana_react/public'; -import { EuiThemeProvider } from '../../../xpack_legacy/common'; import { PluginContext } from '../context/plugin_context'; import { usePluginContext } from '../hooks/use_plugin_context'; import { useRouteParams } from '../hooks/use_route_params'; diff --git a/x-pack/plugins/observability/public/components/shared/core_web_vitals/__stories__/core_vitals.stories.tsx b/x-pack/plugins/observability/public/components/shared/core_web_vitals/__stories__/core_vitals.stories.tsx index 208c840b403e9..b16a560899b52 100644 --- a/x-pack/plugins/observability/public/components/shared/core_web_vitals/__stories__/core_vitals.stories.tsx +++ b/x-pack/plugins/observability/public/components/shared/core_web_vitals/__stories__/core_vitals.stories.tsx @@ -8,10 +8,10 @@ import React, { ComponentType } from 'react'; import { IntlProvider } from 'react-intl'; import { Observable } from 'rxjs'; import { CoreStart } from 'src/core/public'; +import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common'; import { createKibanaReactContext } from '../../../../../../../../src/plugins/kibana_react/public'; import { CoreVitalItem } from '../core_vital_item'; import { LCP_HELP_LABEL, LCP_LABEL } from '../translations'; -import { EuiThemeProvider } from '../../../../typings'; const KibanaReactContext = createKibanaReactContext(({ uiSettings: { get: () => {}, get$: () => new Observable() }, diff --git a/x-pack/plugins/observability/public/hooks/use_theme.tsx b/x-pack/plugins/observability/public/hooks/use_theme.tsx index 51a1ad5029538..ccb8bf6d86be3 100644 --- a/x-pack/plugins/observability/public/hooks/use_theme.tsx +++ b/x-pack/plugins/observability/public/hooks/use_theme.tsx @@ -5,7 +5,7 @@ */ import { useContext } from 'react'; import { ThemeContext } from 'styled-components'; -import { EuiTheme } from '../../../xpack_legacy/common'; +import { EuiTheme } from '../../../../../src/plugins/kibana_react/common'; export function useTheme() { const theme: EuiTheme = useContext(ThemeContext); diff --git a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx index a28e34e7d4dcb..14cf141c7cd38 100644 --- a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx +++ b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx @@ -9,12 +9,12 @@ import { storiesOf } from '@storybook/react'; import { AppMountParameters, CoreStart } from 'kibana/public'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; +import { EuiThemeProvider } from '../../../../../../src/plugins/kibana_react/common'; import { UI_SETTINGS } from '../../../../../../src/plugins/data/public'; import { HasDataContextProvider } from '../../context/has_data_context'; import { PluginContext } from '../../context/plugin_context'; import { registerDataHandler, unregisterDataHandler } from '../../data_handler'; import { ObservabilityPluginSetupDeps } from '../../plugin'; -import { EuiThemeProvider } from '../../typings'; import { OverviewPage } from './'; import { alertsFetchData } from './mock/alerts.mock'; import { emptyResponse as emptyAPMResponse, fetchApmData } from './mock/apm.mock'; diff --git a/x-pack/plugins/observability/public/typings/eui_styled_components.tsx b/x-pack/plugins/observability/public/typings/eui_styled_components.tsx deleted file mode 100644 index 9e547b58bc736..0000000000000 --- a/x-pack/plugins/observability/public/typings/eui_styled_components.tsx +++ /dev/null @@ -1,47 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import * as styledComponents from 'styled-components'; -import { ThemedStyledComponentsModule, ThemeProvider, ThemeProviderProps } from 'styled-components'; - -import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json'; -import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; - -export interface EuiTheme { - eui: typeof euiLightVars | typeof euiDarkVars; - darkMode: boolean; -} - -function EuiThemeProvider< - OuterTheme extends styledComponents.DefaultTheme = styledComponents.DefaultTheme ->({ - darkMode = false, - ...otherProps -}: Omit, 'theme'> & { - darkMode?: boolean; -}) { - return ( - ({ - ...outerTheme, - eui: darkMode ? euiDarkVars : euiLightVars, - darkMode, - })} - /> - ); -} - -const { - default: euiStyled, - css, - createGlobalStyle, - keyframes, - withTheme, -} = (styledComponents as unknown) as ThemedStyledComponentsModule; - -export { css, euiStyled, EuiThemeProvider, createGlobalStyle, keyframes, withTheme }; diff --git a/x-pack/plugins/observability/public/typings/index.ts b/x-pack/plugins/observability/public/typings/index.ts index 5cc2c613881df..a54d514fde18f 100644 --- a/x-pack/plugins/observability/public/typings/index.ts +++ b/x-pack/plugins/observability/public/typings/index.ts @@ -5,5 +5,4 @@ */ export * from './eui_draggable'; -export * from './eui_styled_components'; export * from './fetch_overview_data'; diff --git a/x-pack/plugins/observability/public/utils/test_helper.tsx b/x-pack/plugins/observability/public/utils/test_helper.tsx index 10158d7aedd0e..bd48acf2ab991 100644 --- a/x-pack/plugins/observability/public/utils/test_helper.tsx +++ b/x-pack/plugins/observability/public/utils/test_helper.tsx @@ -12,7 +12,7 @@ import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/p import translations from '../../../translations/translations/ja-JP.json'; import { PluginContext } from '../context/plugin_context'; import { ObservabilityPluginSetupDeps } from '../plugin'; -import { EuiThemeProvider } from '../typings'; +import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common'; const appMountParameters = ({ setHeaderActionMenu: () => {} } as unknown) as AppMountParameters; diff --git a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_root_provider.tsx b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_root_provider.tsx index 149d948a53fc4..49cc345a631dd 100644 --- a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_root_provider.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_root_provider.tsx @@ -11,7 +11,7 @@ import { Router } from 'react-router-dom'; import { History } from 'history'; import useObservable from 'react-use/lib/useObservable'; import { Store } from 'redux'; -import { EuiThemeProvider } from '../../../../../xpack_legacy/common'; +import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common'; import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public'; import { RouteCapture } from '../../components/endpoint/route_capture'; import { StartPlugins } from '../../../types'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/vertical_divider.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/vertical_divider.ts index b6f5c9b7421b5..e989077e1b8cb 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/vertical_divider.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/vertical_divider.ts @@ -5,7 +5,7 @@ */ import styled from 'styled-components'; -import { EuiTheme } from '../../../../../../xpack_legacy/common'; +import { EuiTheme } from '../../../../../../../../src/plugins/kibana_react/common'; type SpacingOptions = keyof EuiTheme['eui']['spacerSizes']; diff --git a/x-pack/plugins/uptime/public/apps/uptime_app.tsx b/x-pack/plugins/uptime/public/apps/uptime_app.tsx index 061398b25e452..13d4e1538dc4a 100644 --- a/x-pack/plugins/uptime/public/apps/uptime_app.tsx +++ b/x-pack/plugins/uptime/public/apps/uptime_app.tsx @@ -29,7 +29,7 @@ import { UptimeAlertsFlyoutWrapper } from '../components/overview/alerts'; import { store } from '../state'; import { kibanaService } from '../state/kibana_service'; import { ActionMenu } from '../components/common/header/action_menu'; -import { EuiThemeProvider } from '../../../observability/public'; +import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common'; export interface UptimeAppColors { danger: string; diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp.tsx index 2a35587a1960f..be4f0fc62271d 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp.tsx @@ -20,7 +20,8 @@ import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n/react'; import { Ping } from '../../../../../common/runtime_types/ping'; import { getShortTimeStamp } from '../../../overview/monitor_list/columns/monitor_status_column'; -import { euiStyled, FETCH_STATUS, useFetcher } from '../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; +import { useFetcher, FETCH_STATUS } from '../../../../../../observability/public'; import { getJourneyScreenshot } from '../../../../state/api/journey'; import { UptimeSettingsContext } from '../../../../contexts'; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts index 7bf5100730f5e..312014dfb35d7 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts @@ -6,7 +6,7 @@ import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { rgba } from 'polished'; -import { euiStyled } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { FIXED_AXIS_HEIGHT } from './constants'; interface WaterfallChartOuterContainerProps { diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall.test.tsx index 44e63f04f7bec..e9694cde61307 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall.test.tsx @@ -13,7 +13,7 @@ import { renderLegendItem, renderSidebarItem, } from '../../step_detail/waterfall/waterfall_chart_wrapper'; -import { EuiThemeProvider } from '../../../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../../../src/plugins/kibana_react/common'; import { WaterfallChartOuterContainer } from './styles'; describe('waterfall', () => { diff --git a/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.tsx b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.tsx index 851da79314552..27fdd1f8071bd 100644 --- a/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.tsx +++ b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.tsx @@ -8,7 +8,7 @@ import React, { useRef, useEffect, RefObject } from 'react'; import { EuiSuggestItem } from '@elastic/eui'; import { QuerySuggestion } from '../../../../../../../../src/plugins/data/public'; -import { euiStyled } from '../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; const SuggestionItem = euiStyled.div<{ selected: boolean }>` background: ${(props) => (props.selected ? props.theme.eui.euiColorLightestShade : 'initial')}; diff --git a/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.tsx b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.tsx index 4f8fb712de679..817845307c40a 100644 --- a/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.tsx +++ b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.tsx @@ -9,7 +9,7 @@ import { isEmpty } from 'lodash'; import { rgba } from 'polished'; import { Suggestion } from './suggestion'; import { QuerySuggestion } from '../../../../../../../../src/plugins/data/public'; -import { euiStyled } from '../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; export const unit = 16; diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.test.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.test.tsx index 41a74fc0aec5c..e882fc730e420 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.test.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { getLocationStatus, MonitorListStatusColumn } from './monitor_status_column'; import { Ping } from '../../../../../common/runtime_types'; import { STATUS } from '../../../../../common/constants'; -import { EuiThemeProvider } from '../../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common'; import { mockDate, mockMoment } from '../../../../lib/helper/test_helpers'; import { render } from '../../../../lib/helper/rtl_helpers'; import { fireEvent, screen, waitFor } from '@testing-library/react'; diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.tsx index 81e486bb467bb..ce446db17dcd4 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.tsx @@ -19,7 +19,7 @@ import { } from '../../../../../common/constants'; import { UptimeThemeContext } from '../../../../contexts'; -import { euiStyled } from '../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; import { STATUS_DOWN_LABEL, STATUS_UP_LABEL } from '../../../common/translations'; interface MonitorListStatusColumnProps { diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.test.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.test.tsx index 82a7100a48081..bec90191d1105 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.test.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.test.tsx @@ -19,7 +19,7 @@ import * as redux from 'react-redux'; import moment from 'moment'; import { IHttpFetchError } from '../../../../../../../src/core/public'; import { mockMoment } from '../../../lib/helper/test_helpers'; -import { EuiThemeProvider } from '../../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common'; jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => { return { diff --git a/x-pack/plugins/uptime/public/lib/helper/rtl_helpers.tsx b/x-pack/plugins/uptime/public/lib/helper/rtl_helpers.tsx index 97bd63614141b..78f553121a3fd 100644 --- a/x-pack/plugins/uptime/public/lib/helper/rtl_helpers.tsx +++ b/x-pack/plugins/uptime/public/lib/helper/rtl_helpers.tsx @@ -13,7 +13,7 @@ import { I18nProvider } from '@kbn/i18n/react'; import { coreMock } from 'src/core/public/mocks'; import { configure } from '@testing-library/dom'; import { mockState } from '../__mocks__/uptime_store.mock'; -import { EuiThemeProvider } from '../../../../observability/public'; +import { EuiThemeProvider } from '../../../../../../src/plugins/kibana_react/common'; import { KibanaContextProvider, KibanaServices, diff --git a/x-pack/plugins/xpack_legacy/common/index.ts b/x-pack/plugins/xpack_legacy/common/index.ts deleted file mode 100644 index 8c0dace27faf4..0000000000000 --- a/x-pack/plugins/xpack_legacy/common/index.ts +++ /dev/null @@ -1,7 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -export * from './eui_styled_components'; From 474af9f3ebcf506a3f2076a1491c250bc5e0810f Mon Sep 17 00:00:00 2001 From: Constance Date: Mon, 25 Jan 2021 11:56:42 -0800 Subject: [PATCH 46/62] [Enterprise Search] Add Kea test helper for directly accessing listeners (#89061) * Add getListeners to Kea test helpers * Update TelemetryLogic to use new getListeners helper Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/applications/__mocks__/kea.mock.ts | 25 +++++++++++++++++++ .../shared/telemetry/telemetry_logic.test.ts | 16 ++++-------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea.mock.ts index 78ffbcfa3526f..5ac8cce04181e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea.mock.ts @@ -120,4 +120,29 @@ export class LogicMounter { public unmount = () => { this.unmountFn(); }; + + /** + * Some tests (e.g. async tests, tests that expect thrown errors) need to access + * listener functions directly instead of calling `SomeLogic.actions.someListener`, + * due to how Kea invokes/wraps action fns by design. + * + * Example usage: + * + * const { mount, getListeners } = new LogicMounter(SomeLogic); + * + * it('some test', async () => { + * mount(); + * const { someListener } = getListeners({ values: { someMockValue: false } }); + * + * const mockBreakpoint = jest.fn(); + * await someListener({ someMockArgument: true }, mockBreakpoint); + * }); + */ + public getListeners = (listenersArgs: object = {}) => { + const { listeners } = this.logicFile.inputs[0]; + + return typeof listeners === 'function' + ? (listeners as Function)(listenersArgs) // e.g., listeners({ values, actions, props }) => ({ ... }) + : listeners; // handles simpler logic files that just define listeners: { ... } + }; } diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/telemetry_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/telemetry_logic.test.ts index 6d4e4f4fe649c..770dcf074f163 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/telemetry_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/telemetry_logic.test.ts @@ -4,20 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import { resetContext } from 'kea'; - import { JSON_HEADER as headers } from '../../../../common/constants'; -import { mockHttpValues } from '../../__mocks__/http_logic.mock'; +import { LogicMounter, mockHttpValues } from '../../__mocks__'; -import { TelemetryLogic } from './'; +import { TelemetryLogic } from './telemetry_logic'; describe('Telemetry logic', () => { + const { mount, getListeners } = new LogicMounter(TelemetryLogic); const { http } = mockHttpValues; beforeEach(() => { jest.clearAllMocks(); - resetContext({}); - TelemetryLogic.mount(); + mount(); }); describe('sendTelemetry', () => { @@ -36,11 +34,7 @@ describe('Telemetry logic', () => { it('throws an error if the telemetry endpoint fails', async () => { http.put.mockImplementationOnce(() => Promise.reject()); - - // To capture thrown errors, we have to call the listener fn directly - // instead of using `TelemetryLogic.actions.sendTelemetry` - this is - // due to how Kea invokes/wraps action fns by design. - const { sendTelemetry } = (TelemetryLogic.inputs[0] as any).listeners({ actions: {} }); + const { sendTelemetry } = getListeners(); await expect(sendTelemetry({ action: '', metric: '', product: '' })).rejects.toThrow( 'Unable to send telemetry' From a31c3eba13f4fb7951a7865f9b2efd4220b30bc8 Mon Sep 17 00:00:00 2001 From: Constance Date: Mon, 25 Jan 2021 12:24:04 -0800 Subject: [PATCH 47/62] [App Search] Add AnalyticsCards & AnalyticsChart components to analytics pages (#88930) * Create reusable AnalyticsCards component * Update EngineOverview to use new AnalyticsCards component * Update Analytics overview with AnalyticsCards + data * Update QueryDetail with AnalyticsCards + data * Update Analytics overview with AnalyticsChart + data - turns out we do need startDate after all for charts, so I added it back to types * Update QueryDetail with AnalyticsChart + data * [Polish] Dash click and no result lines to match standalone UI Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../analytics/analytics_logic.test.ts | 25 +++++++- .../components/analytics/analytics_logic.ts | 55 ++++++++++++++++ .../components/analytics_cards.test.tsx | 38 ++++++++++++ .../analytics/components/analytics_cards.tsx | 32 ++++++++++ .../components/analytics_chart.test.tsx | 8 +++ .../analytics/components/analytics_chart.tsx | 4 +- .../components/analytics/components/index.ts | 1 + .../components/analytics/constants.ts | 4 ++ .../app_search/components/analytics/index.ts | 2 +- .../app_search/components/analytics/types.ts | 3 +- .../analytics/views/analytics.test.tsx | 15 ++++- .../components/analytics/views/analytics.tsx | 62 ++++++++++++++++++- .../analytics/views/query_detail.test.tsx | 10 +++ .../analytics/views/query_detail.tsx | 35 +++++++++++ .../components/total_stats.test.tsx | 36 ++--------- .../components/total_stats.tsx | 38 ++++++------ 16 files changed, 312 insertions(+), 56 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts index 30a64219403b5..0901ff2737803 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.test.ts @@ -29,6 +29,15 @@ describe('AnalyticsLogic', () => { dataLoading: true, analyticsUnavailable: false, allTags: [], + totalQueries: 0, + totalQueriesNoResults: 0, + totalClicks: 0, + totalQueriesForQuery: 0, + queriesPerDay: [], + queriesNoResultsPerDay: [], + clicksPerDay: [], + queriesPerDayForQuery: [], + startDate: '', }; const MOCK_TOP_QUERIES = [ @@ -66,6 +75,7 @@ describe('AnalyticsLogic', () => { const MOCK_ANALYTICS_RESPONSE = { analyticsUnavailable: false, allTags: ['some-tag'], + startDate: '1970-01-01', recentQueries: MOCK_RECENT_QUERIES, topQueries: MOCK_TOP_QUERIES, topQueriesNoResults: MOCK_TOP_QUERIES, @@ -81,6 +91,7 @@ describe('AnalyticsLogic', () => { const MOCK_QUERY_RESPONSE = { analyticsUnavailable: false, allTags: ['some-tag'], + startDate: '1970-01-01', totalQueriesForQuery: 50, queriesPerDayForQuery: [25, 0, 25], topClicksForQuery: MOCK_TOP_CLICKS, @@ -120,7 +131,14 @@ describe('AnalyticsLogic', () => { dataLoading: false, analyticsUnavailable: false, allTags: ['some-tag'], - // TODO: more state will get set here in future PRs + startDate: '1970-01-01', + totalClicks: 1000, + totalQueries: 5000, + totalQueriesNoResults: 500, + queriesPerDay: [10, 50, 100], + queriesNoResultsPerDay: [1, 2, 3], + clicksPerDay: [0, 10, 50], + // TODO: Replace this with ...MOCK_ANALYTICS_RESPONSE once all data is set }); }); }); @@ -135,7 +153,10 @@ describe('AnalyticsLogic', () => { dataLoading: false, analyticsUnavailable: false, allTags: ['some-tag'], - // TODO: more state will get set here in future PRs + startDate: '1970-01-01', + totalQueriesForQuery: 50, + queriesPerDayForQuery: [25, 0, 25], + // TODO: Replace this with ...MOCK_QUERY_RESPONSE once all data is set }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.ts index 4d8603f4bb6d6..537de02a0fee5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_logic.ts @@ -62,6 +62,61 @@ export const AnalyticsLogic = kea allTags, }, ], + totalQueries: [ + 0, + { + onAnalyticsDataLoad: (_, { totalQueries }) => totalQueries, + }, + ], + totalQueriesNoResults: [ + 0, + { + onAnalyticsDataLoad: (_, { totalQueriesNoResults }) => totalQueriesNoResults, + }, + ], + totalClicks: [ + 0, + { + onAnalyticsDataLoad: (_, { totalClicks }) => totalClicks, + }, + ], + queriesPerDay: [ + [], + { + onAnalyticsDataLoad: (_, { queriesPerDay }) => queriesPerDay, + }, + ], + queriesNoResultsPerDay: [ + [], + { + onAnalyticsDataLoad: (_, { queriesNoResultsPerDay }) => queriesNoResultsPerDay, + }, + ], + clicksPerDay: [ + [], + { + onAnalyticsDataLoad: (_, { clicksPerDay }) => clicksPerDay, + }, + ], + totalQueriesForQuery: [ + 0, + { + onQueryDataLoad: (_, { totalQueriesForQuery }) => totalQueriesForQuery, + }, + ], + queriesPerDayForQuery: [ + [], + { + onQueryDataLoad: (_, { queriesPerDayForQuery }) => queriesPerDayForQuery, + }, + ], + startDate: [ + '', + { + onAnalyticsDataLoad: (_, { startDate }) => startDate, + onQueryDataLoad: (_, { startDate }) => startDate, + }, + ], }), listeners: ({ actions }) => ({ loadAnalyticsData: async () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.test.tsx new file mode 100644 index 0000000000000..eee6517d5b480 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.test.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; +import { EuiStat } from '@elastic/eui'; + +import { AnalyticsCards } from './'; + +describe('AnalyticsCards', () => { + it('renders', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(EuiStat)).toHaveLength(2); + expect(wrapper.find('[data-test-subj="RedFish"]').prop('title')).toEqual(100); + expect(wrapper.find('[data-test-subj="RedFish"]').prop('description')).toEqual('Red fish'); + expect(wrapper.find('[data-test-subj="BlueFish"]').prop('title')).toEqual(2000); + expect(wrapper.find('[data-test-subj="BlueFish"]').prop('description')).toEqual('Blue fish'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.tsx new file mode 100644 index 0000000000000..0359283059171 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_cards.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiStat } from '@elastic/eui'; + +interface Props { + stats: Array<{ + text: string; + stat: number; + dataTestSubj?: string; + }>; +} +export const AnalyticsCards: React.FC = ({ stats }) => ( + + {stats.map(({ text, stat, dataTestSubj }) => ( + + + + + + ))} + +); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.test.tsx index 4e071ac7982bd..b2e3e615e4817 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.test.tsx @@ -54,6 +54,14 @@ describe('AnalyticsChart', () => { expect(wrapper.find(LineSeries)).toHaveLength(3); }); + it('renders dashed lines', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(LineSeries).prop('lineSeriesStyle')?.line?.dash).toBeTruthy(); + }); + it('formats x-axis dates correctly', () => { const wrapper = shallow(); const dateFormatter: Function = wrapper.find('#bottom-axis').prop('tickFormat'); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.tsx index 02ad2dff92827..a73d3c9054ed6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.tsx @@ -25,6 +25,7 @@ interface Props { lines: Array<{ id: string; data: ChartData; + isDashed?: boolean; }>; } export const AnalyticsChart: React.FC = ({ height = 300, lines }) => { @@ -39,7 +40,7 @@ export const AnalyticsChart: React.FC = ({ height = 300, lines }) => { headerFormatter: (tooltip) => moment(tooltip.value).format(TOOLTIP_DATE_FORMAT), }} /> - {lines.map(({ id, data }) => ( + {lines.map(({ id, data, isDashed }) => ( = ({ height = 300, lines }) => { xAccessor={'x'} yAccessors={['y']} curve={CurveType.CURVE_MONOTONE_X} + lineSeriesStyle={isDashed ? { line: { dash: [5, 5] } } : undefined} /> ))} { it('renders', () => { + setMockValues({ + totalQueries: 3, + totalQueriesNoResults: 2, + totalClicks: 1, + queriesPerDay: [10, 20, 30], + queriesNoResultsPerDay: [1, 2, 3], + clicksPerDay: [0, 1, 5], + startDate: '1970-01-01', + }); const wrapper = shallow(); - expect(wrapper.isEmptyRender()).toBe(false); // TODO + expect(wrapper.find(AnalyticsCards)).toHaveLength(1); + expect(wrapper.find(AnalyticsChart)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/analytics.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/analytics.tsx index 5febeae203aba..d3c3bff5a2947 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/analytics.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/analytics.tsx @@ -5,13 +5,73 @@ */ import React from 'react'; +import { useValues } from 'kea'; -import { ANALYTICS_TITLE } from '../constants'; +import { EuiSpacer } from '@elastic/eui'; + +import { + ANALYTICS_TITLE, + TOTAL_QUERIES, + TOTAL_QUERIES_NO_RESULTS, + TOTAL_CLICKS, +} from '../constants'; import { AnalyticsLayout } from '../analytics_layout'; +import { AnalyticsLogic, AnalyticsCards, AnalyticsChart, convertToChartData } from '../'; export const Analytics: React.FC = () => { + const { + totalQueries, + totalQueriesNoResults, + totalClicks, + queriesPerDay, + queriesNoResultsPerDay, + clicksPerDay, + startDate, + } = useValues(AnalyticsLogic); + return ( + + + + + +

TODO: Analytics overview

); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.test.tsx index 2e4bd36d79382..99485340f6b88 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.test.tsx @@ -5,6 +5,7 @@ */ import '../../../../__mocks__/react_router_history.mock'; +import { setMockValues } from '../../../../__mocks__'; import React from 'react'; import { useParams } from 'react-router-dom'; @@ -12,6 +13,7 @@ import { shallow } from 'enzyme'; import { SetAppSearchChrome as SetPageChrome } from '../../../../shared/kibana_chrome'; +import { AnalyticsCards, AnalyticsChart } from '../components'; import { QueryDetail } from './'; describe('QueryDetail', () => { @@ -19,6 +21,11 @@ describe('QueryDetail', () => { beforeEach(() => { (useParams as jest.Mock).mockReturnValueOnce({ query: 'some-query' }); + + setMockValues({ + totalQueriesForQuery: 100, + queriesPerDayForQuery: [0, 5, 10], + }); }); it('renders', () => { @@ -31,5 +38,8 @@ describe('QueryDetail', () => { 'Query', 'some-query', ]); + + expect(wrapper.find(AnalyticsCards)).toHaveLength(1); + expect(wrapper.find(AnalyticsChart)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.tsx index b3b7e5258c536..53c1dc8b845b1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/views/query_detail.tsx @@ -6,12 +6,16 @@ import React from 'react'; import { useParams } from 'react-router-dom'; +import { useValues } from 'kea'; + import { i18n } from '@kbn/i18n'; +import { EuiSpacer } from '@elastic/eui'; import { SetAppSearchChrome as SetPageChrome } from '../../../../shared/kibana_chrome'; import { BreadcrumbTrail } from '../../../../shared/kibana_chrome/generate_breadcrumbs'; import { AnalyticsLayout } from '../analytics_layout'; +import { AnalyticsLogic, AnalyticsCards, AnalyticsChart, convertToChartData } from '../'; const QUERY_DETAIL_TITLE = i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.analytics.queryDetail.title', @@ -24,10 +28,41 @@ interface Props { export const QueryDetail: React.FC = ({ breadcrumbs }) => { const { query } = useParams() as { query: string }; + const { totalQueriesForQuery, queriesPerDayForQuery, startDate } = useValues(AnalyticsLogic); + return ( + + + + + +

TODO: Query detail page

); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx index 6cb47e8b419f3..2464eb258452e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx @@ -7,45 +7,19 @@ import { setMockValues } from '../../../../__mocks__/kea.mock'; import React from 'react'; -import { shallow, ShallowWrapper } from 'enzyme'; -import { EuiStat } from '@elastic/eui'; +import { shallow } from 'enzyme'; +import { AnalyticsCards } from '../../analytics'; import { TotalStats } from './total_stats'; describe('TotalStats', () => { - let wrapper: ShallowWrapper; - - beforeAll(() => { - jest.clearAllMocks(); + it('renders', () => { setMockValues({ totalQueries: 11, documentCount: 22, totalClicks: 33, }); - wrapper = shallow(); - }); - - it('renders the total queries stat', () => { - expect(wrapper.find('[data-test-subj="TotalQueriesCard"]')).toHaveLength(1); - - const card = wrapper.find(EuiStat).at(0); - expect(card.prop('title')).toEqual(11); - expect(card.prop('description')).toEqual('Total queries'); - }); - - it('renders the total documents stat', () => { - expect(wrapper.find('[data-test-subj="TotalDocumentsCard"]')).toHaveLength(1); - - const card = wrapper.find(EuiStat).at(1); - expect(card.prop('title')).toEqual(22); - expect(card.prop('description')).toEqual('Total documents'); - }); - - it('renders the total clicks stat', () => { - expect(wrapper.find('[data-test-subj="TotalClicksCard"]')).toHaveLength(1); - - const card = wrapper.find(EuiStat).at(2); - expect(card.prop('title')).toEqual(33); - expect(card.prop('description')).toEqual('Total clicks'); + const wrapper = shallow(); + expect(wrapper.find(AnalyticsCards)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx index a27142938f558..a7f5923498455 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx @@ -6,9 +6,9 @@ import React from 'react'; import { useValues } from 'kea'; -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiStat } from '@elastic/eui'; import { TOTAL_QUERIES, TOTAL_DOCUMENTS, TOTAL_CLICKS } from '../../analytics/constants'; +import { AnalyticsCards } from '../../analytics'; import { EngineOverviewLogic } from '../'; @@ -16,22 +16,24 @@ export const TotalStats: React.FC = () => { const { totalQueries, documentCount, totalClicks } = useValues(EngineOverviewLogic); return ( - - - - - - - - - - - - - - - - - + ); }; From ac06965cb089d058cc1e7de74d39b5dcf3ad9f74 Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Mon, 25 Jan 2021 14:52:12 -0600 Subject: [PATCH 48/62] [DOCS] Fixes the version file (#89220) --- docs/index.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.asciidoc b/docs/index.asciidoc index b91af2ab51ebf..eb6f794434f8a 100644 --- a/docs/index.asciidoc +++ b/docs/index.asciidoc @@ -7,7 +7,7 @@ :blog-ref: https://www.elastic.co/blog/ :wikipedia: https://en.wikipedia.org/wiki -include::{docs-root}/shared/versions/stack/7.10.asciidoc[] +include::{docs-root}/shared/versions/stack/{source_branch}.asciidoc[] :docker-repo: docker.elastic.co/kibana/kibana :docker-image: docker.elastic.co/kibana/kibana:{version} From 5d68b101067ab80aa36e2a85bfd2b2b6df422e91 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Mon, 25 Jan 2021 14:39:20 -0700 Subject: [PATCH 49/62] Upgrade EUI to v31.3.0 (#88881) * Bump EUI to v31.3.0 * jest snapshot updates * Fixed space issue in kbnQueryBar date picker * Removed unecessary space in query bar scss Co-authored-by: miukimiu Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 2 +- .../ui/query_string_input/_query_bar.scss | 3 +- .../__snapshots__/cron_editor.test.tsx.snap | 76 +++++++++++++++++++ .../sample_data_view_data_button.test.js.snap | 1 + .../share_context_menu.test.tsx.snap | 3 + .../extended_template.stories.storyshot | 2 + .../date_format.stories.storyshot | 3 + .../number_format.stories.storyshot | 3 + .../saved_elements_modal.stories.storyshot | 3 + .../__snapshots__/tag.stories.storyshot | 4 +- .../__snapshots__/tag_list.stories.storyshot | 6 +- .../text_style_picker.stories.storyshot | 2 + .../workpad_templates.stories.storyshot | 9 ++- .../extended_template.stories.storyshot | 6 ++ .../extended_template.stories.storyshot | 4 + .../__snapshots__/tools_control.test.tsx.snap | 2 + .../toc_entry_actions_popover.test.tsx.snap | 4 + .../remote_cluster_form.test.js.snap | 1 + .../__snapshots__/index.test.tsx.snap | 1 + .../__snapshots__/cert_status.test.tsx.snap | 2 +- .../__snapshots__/donut_chart.test.tsx.snap | 8 +- yarn.lock | 8 +- 22 files changed, 133 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 24297011ccc63..dac83dacf6fbf 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "@elastic/datemath": "link:packages/elastic-datemath", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary", "@elastic/ems-client": "7.11.0", - "@elastic/eui": "31.0.0", + "@elastic/eui": "31.3.0", "@elastic/filesaver": "1.1.2", "@elastic/good": "^9.0.1-kibana3", "@elastic/node-crypto": "1.2.1", diff --git a/src/plugins/data/public/ui/query_string_input/_query_bar.scss b/src/plugins/data/public/ui/query_string_input/_query_bar.scss index 3e3982dd58e57..65f652df31d0c 100644 --- a/src/plugins/data/public/ui/query_string_input/_query_bar.scss +++ b/src/plugins/data/public/ui/query_string_input/_query_bar.scss @@ -73,9 +73,10 @@ // sass-lint:disable-block no-important flex-grow: 0 !important; flex-basis: auto !important; - margin-right: -$euiSizeXS !important; &.kbnQueryBar__datePickerWrapper-isHidden { + // sass-lint:disable-block no-important + margin-right: -$euiSizeXS !important; width: 0; overflow: hidden; max-width: 0; diff --git a/src/plugins/es_ui_shared/public/components/cron_editor/__snapshots__/cron_editor.test.tsx.snap b/src/plugins/es_ui_shared/public/components/cron_editor/__snapshots__/cron_editor.test.tsx.snap index 9207c6467f6a9..151bd91750daa 100644 --- a/src/plugins/es_ui_shared/public/components/cron_editor/__snapshots__/cron_editor.test.tsx.snap +++ b/src/plugins/es_ui_shared/public/components/cron_editor/__snapshots__/cron_editor.test.tsx.snap @@ -170,6 +170,7 @@ exports[`CronEditor is rendered with a DAY frequency 1`] = `
@@ -139,6 +140,7 @@ exports[`Storyshots arguments/AxisConfig/components extended 1`] = ` aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" />
diff --git a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/date_format/__stories__/__snapshots__/date_format.stories.storyshot b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/date_format/__stories__/__snapshots__/date_format.stories.storyshot index 8f0a8b5abd9b5..238fe7c259c6e 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/date_format/__stories__/__snapshots__/date_format.stories.storyshot +++ b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/date_format/__stories__/__snapshots__/date_format.stories.storyshot @@ -46,6 +46,7 @@ Array [ aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" />
@@ -118,6 +119,7 @@ Array [ aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" />
@@ -189,6 +191,7 @@ exports[`Storyshots arguments/DateFormat with preset format 1`] = ` aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" />
diff --git a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/__stories__/__snapshots__/number_format.stories.storyshot b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/__stories__/__snapshots__/number_format.stories.storyshot index 17f158c680a67..2159e49e2bcf1 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/__stories__/__snapshots__/number_format.stories.storyshot +++ b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/__stories__/__snapshots__/number_format.stories.storyshot @@ -56,6 +56,7 @@ Array [ aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" /> @@ -138,6 +139,7 @@ Array [ aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" /> @@ -219,6 +221,7 @@ exports[`Storyshots arguments/NumberFormat with preset format 1`] = ` aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" /> diff --git a/x-pack/plugins/canvas/public/components/saved_elements_modal/__stories__/__snapshots__/saved_elements_modal.stories.storyshot b/x-pack/plugins/canvas/public/components/saved_elements_modal/__stories__/__snapshots__/saved_elements_modal.stories.storyshot index 900665fc1ddec..b833120f84f39 100644 --- a/x-pack/plugins/canvas/public/components/saved_elements_modal/__stories__/__snapshots__/saved_elements_modal.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/saved_elements_modal/__stories__/__snapshots__/saved_elements_modal.stories.storyshot @@ -75,6 +75,7 @@ exports[`Storyshots components/SavedElementsModal no custom elements 1`] = ` aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="search" + size="m" /> @@ -222,6 +223,7 @@ exports[`Storyshots components/SavedElementsModal with custom elements 1`] = ` aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="search" + size="m" /> @@ -669,6 +671,7 @@ exports[`Storyshots components/SavedElementsModal with text filter 1`] = ` aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="search" + size="m" /> diff --git a/x-pack/plugins/canvas/public/components/tag/__stories__/__snapshots__/tag.stories.storyshot b/x-pack/plugins/canvas/public/components/tag/__stories__/__snapshots__/tag.stories.storyshot index 6f88120fb2b84..f21ffcf1a70ea 100644 --- a/x-pack/plugins/canvas/public/components/tag/__stories__/__snapshots__/tag.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/tag/__stories__/__snapshots__/tag.stories.storyshot @@ -46,7 +46,7 @@ exports[`Storyshots components/Tags/Tag as badge with color 1`] = ` exports[`Storyshots components/Tags/Tag as health 1`] = `
,
,
@@ -587,6 +588,7 @@ exports[`Storyshots components/TextStylePicker interactive 1`] = ` aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" />
diff --git a/x-pack/plugins/canvas/public/components/workpad_templates/examples/__snapshots__/workpad_templates.stories.storyshot b/x-pack/plugins/canvas/public/components/workpad_templates/examples/__snapshots__/workpad_templates.stories.storyshot index d267ba07078fe..489827246e998 100644 --- a/x-pack/plugins/canvas/public/components/workpad_templates/examples/__snapshots__/workpad_templates.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/workpad_templates/examples/__snapshots__/workpad_templates.stories.storyshot @@ -38,6 +38,7 @@ exports[`Storyshots components/WorkpadTemplates default 1`] = ` aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="search" + size="m" />
@@ -319,7 +320,7 @@ exports[`Storyshots components/WorkpadTemplates default 1`] = ` className="euiTableCellContent euiTableCellContent--overflowingContent" >
@@ -206,6 +207,7 @@ exports[`Storyshots arguments/ContainerStyle extended 1`] = ` aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" />
@@ -547,6 +549,7 @@ exports[`Storyshots arguments/ContainerStyle/components appearance form 1`] = ` aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" />
@@ -611,6 +614,7 @@ exports[`Storyshots arguments/ContainerStyle/components appearance form 1`] = ` aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" />
@@ -962,6 +966,7 @@ exports[`Storyshots arguments/ContainerStyle/components extended template 1`] = aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" />
@@ -1026,6 +1031,7 @@ exports[`Storyshots arguments/ContainerStyle/components extended template 1`] = aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" />
diff --git a/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/__stories__/__snapshots__/extended_template.stories.storyshot b/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/__stories__/__snapshots__/extended_template.stories.storyshot index 5d666282f9d0e..05add24cf775c 100644 --- a/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/__stories__/__snapshots__/extended_template.stories.storyshot +++ b/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/__stories__/__snapshots__/extended_template.stories.storyshot @@ -69,6 +69,7 @@ exports[`Storyshots arguments/SeriesStyle extended 1`] = ` aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" />
@@ -161,6 +162,7 @@ exports[`Storyshots arguments/SeriesStyle extended 1`] = ` aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" />
@@ -245,6 +247,7 @@ exports[`Storyshots arguments/SeriesStyle extended 1`] = ` aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" />
@@ -329,6 +332,7 @@ exports[`Storyshots arguments/SeriesStyle extended 1`] = ` aria-hidden="true" className="euiFormControlLayoutCustomIcon__icon" data-euiicon-type="arrowDown" + size="s" /> diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.tsx.snap index a57fcaa393b89..69faf78524326 100644 --- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.tsx.snap +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.tsx.snap @@ -110,6 +110,7 @@ exports[`Should render cancel button when drawing 1`] = ` }, ] } + size="m" /> @@ -235,6 +236,7 @@ exports[`renders 1`] = ` }, ] } + size="m" /> `; diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap index ea37e76bc8494..994e152c2ac4d 100644 --- a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap @@ -109,6 +109,7 @@ exports[`TOCEntryActionsPopover is rendered 1`] = ` }, ] } + size="m" /> `; @@ -222,6 +223,7 @@ exports[`TOCEntryActionsPopover should disable fit to data when supportsFitToBou }, ] } + size="m" /> `; @@ -336,6 +338,7 @@ exports[`TOCEntryActionsPopover should have "show layer" action when layer is no }, ] } + size="m" /> `; @@ -418,6 +421,7 @@ exports[`TOCEntryActionsPopover should not show edit actions in read only mode 1 }, ] } + size="m" /> `; diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap index 177ef8b3c6cad..5f09193be90c2 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap @@ -936,6 +936,7 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u /> diff --git a/x-pack/plugins/security_solution/public/common/components/paginated_table/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/paginated_table/__snapshots__/index.test.tsx.snap index a2a9c30ca4e1c..778916ad2d07a 100644 --- a/x-pack/plugins/security_solution/public/common/components/paginated_table/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/paginated_table/__snapshots__/index.test.tsx.snap @@ -355,6 +355,7 @@ exports[`Paginated Table Component rendering it renders the default load more ta "small": "14px", "xSmall": "12px", }, + "euiMarkdownEditorMinHeight": "150px", "euiPageBackgroundColor": "#1a1b20", "euiPaletteColorBlind": Object { "euiColorVis0": Object { diff --git a/x-pack/plugins/uptime/public/components/certificates/__snapshots__/cert_status.test.tsx.snap b/x-pack/plugins/uptime/public/components/certificates/__snapshots__/cert_status.test.tsx.snap index 0a84278c32018..79f58ee76d18f 100644 --- a/x-pack/plugins/uptime/public/components/certificates/__snapshots__/cert_status.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/certificates/__snapshots__/cert_status.test.tsx.snap @@ -7,7 +7,7 @@ exports[`CertStatus renders expected elements for valid props 1`] = ` }
Date: Mon, 25 Jan 2021 16:43:46 -0500 Subject: [PATCH 50/62] [alerts] adds support for index threshold index param string type (#88540) resolves https://github.com/elastic/kibana/issues/68575 The index threshold alert defines an `index` parameter which is typed as `string | string[]`. However the UI for this alert has been typing it as only `string[]`. This PR changes the UI to work with an incoming string value for this parameter. If the parameter is edited in the UI, it will always be set as an array, even if there is only one element. --- .../alert_types/threshold/expression.tsx | 21 ++++++++++++------- .../public/alert_types/threshold/types.ts | 2 +- .../alert_types/threshold/validation.test.ts | 2 +- .../apps/triggers_actions_ui/details.ts | 2 +- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx index 35b62d1f73f20..8348a797972ae 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx @@ -75,6 +75,12 @@ function isString(value: unknown): value is string { return typeof value === 'string'; } +// normalize the `index` parameter to be a string array +function indexParamToArray(index: string | string[]): string[] { + if (!index) return []; + return isString(index) ? [index] : index; +} + export const IndexThresholdAlertTypeExpression: React.FunctionComponent< AlertTypeParamsExpressionProps > = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, charts, data }) => { @@ -92,6 +98,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< timeWindowUnit, } = alertParams; + const indexArray = indexParamToArray(index); const { http } = useKibana().services; const [indexPopoverOpen, setIndexPopoverOpen] = useState(false); @@ -131,8 +138,8 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< threshold: threshold ?? DEFAULT_VALUES.THRESHOLD, }); - if (index && index.length > 0) { - const currentEsFields = await getFields(http, index); + if (indexArray.length > 0) { + const currentEsFields = await getFields(http, indexArray); const timeFields = getTimeFieldOptions(currentEsFields); setEsFields(currentEsFields); @@ -170,7 +177,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< defaultMessage="Indices to query" /> } - isInvalid={errors.index.length > 0 && index !== undefined} + isInvalid={errors.index.length > 0 && indexArray.length > 0} error={errors.index} helpText={ 0 && index !== undefined} + isInvalid={errors.index.length > 0 && indexArray.length > 0} noSuggestions={!indexOptions.length} options={indexOptions} data-test-subj="thresholdIndexesComboBox" - selectedOptions={(index || []).map((anIndex: string) => { + selectedOptions={indexArray.map((anIndex: string) => { return { label: anIndex, value: anIndex, @@ -306,12 +313,12 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< description={i18n.translate('xpack.stackAlerts.threshold.ui.alertParams.indexLabel', { defaultMessage: 'index', })} - value={index && index.length > 0 ? renderIndices(index) : firstFieldOption.text} + value={indexArray.length > 0 ? renderIndices(indexArray) : firstFieldOption.text} isActive={indexPopoverOpen} onClick={() => { setIndexPopoverOpen(true); }} - isInvalid={!(index && index.length > 0 && timeField !== '')} + isInvalid={!(indexArray.length > 0 && timeField !== '')} /> } isOpen={indexPopoverOpen} diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts b/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts index 4868b92feaeb9..32460c19ebdf0 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts @@ -27,7 +27,7 @@ export interface GroupByType { } export interface IndexThresholdAlertParams extends AlertTypeParams { - index: string[]; + index: string | string[]; timeField?: string; aggType: string; aggField?: string; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.test.ts b/x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.test.ts index 1f24a094d0ece..d8f2d24b106f5 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.test.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.test.ts @@ -32,7 +32,7 @@ describe('expression params validation', () => { }); test('if aggField property is invalid should return proper error message', () => { const initialParams: IndexThresholdAlertParams = { - index: ['test'], + index: 'test', aggType: 'avg', threshold: [], timeWindowSize: 1, diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts index 1ed0b38a238b8..35e2e03969023 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts @@ -228,7 +228,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { timeWindowUnit: 'm', groupBy: 'all', threshold: [1000, 5000], - index: ['.kibana_1'], + index: '.kibana_1', timeField: 'alert', }, actions: [ From 2ae2692f2babc2a13165cff0fb69a625acd017cc Mon Sep 17 00:00:00 2001 From: Phillip Burch Date: Mon, 25 Jan 2021 16:04:34 -0600 Subject: [PATCH 51/62] Add null check for empty process data (#89187) --- .../infra/server/lib/host_details/process_list.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/infra/server/lib/host_details/process_list.ts b/x-pack/plugins/infra/server/lib/host_details/process_list.ts index e9d35f3601634..267245098b17a 100644 --- a/x-pack/plugins/infra/server/lib/host_details/process_list.ts +++ b/x-pack/plugins/infra/server/lib/host_details/process_list.ts @@ -125,9 +125,12 @@ export const getProcessList = async ( command: bucket.key, }; }); - const { - summary, - } = result.aggregations!.summaryEvent.summary.hits.hits[0]._source.system.process; + + let summary: { [p: string]: number } = {}; + if (result.aggregations!.summaryEvent.summary.hits.hits.length) { + summary = result.aggregations!.summaryEvent.summary.hits.hits[0]._source.system.process + .summary; + } return { processList, From fb536f54286a20766a87a88698ca038716e767cb Mon Sep 17 00:00:00 2001 From: Andrea Del Rio Date: Mon, 25 Jan 2021 14:14:47 -0800 Subject: [PATCH 52/62] Make toolbar_button a shared component (#88386) --- src/plugins/kibana_react/public/index.ts | 1 + .../toolbar_button.test.tsx.snap | 199 ++++++++++++++++++ .../public/toolbar_button/index.ts | 9 + .../toolbar_button}/toolbar_button.scss | 19 +- .../toolbar_button/toolbar_button.test.tsx | 47 +++++ .../public/toolbar_button}/toolbar_button.tsx | 32 ++- .../config_panel/layer_settings.tsx | 2 +- .../workspace_panel/chart_switch.tsx | 2 +- .../change_indexpattern.tsx | 2 +- .../lens/public/shared_components/index.ts | 1 - .../shared_components/toolbar_popover.tsx | 2 +- .../axis_settings_popover.tsx | 3 +- 12 files changed, 292 insertions(+), 27 deletions(-) create mode 100644 src/plugins/kibana_react/public/toolbar_button/__snapshots__/toolbar_button.test.tsx.snap create mode 100644 src/plugins/kibana_react/public/toolbar_button/index.ts rename {x-pack/plugins/lens/public/shared_components => src/plugins/kibana_react/public/toolbar_button}/toolbar_button.scss (81%) create mode 100644 src/plugins/kibana_react/public/toolbar_button/toolbar_button.test.tsx rename {x-pack/plugins/lens/public/shared_components => src/plugins/kibana_react/public/toolbar_button}/toolbar_button.tsx (61%) diff --git a/src/plugins/kibana_react/public/index.ts b/src/plugins/kibana_react/public/index.ts index c99da5e9b36b8..cbb60bba47861 100644 --- a/src/plugins/kibana_react/public/index.ts +++ b/src/plugins/kibana_react/public/index.ts @@ -15,6 +15,7 @@ export * from './ui_settings'; export * from './field_icon'; export * from './field_button'; export * from './table_list_view'; +export * from './toolbar_button'; export * from './split_panel'; export * from './react_router_navigate'; export { ValidatedDualRange, Value } from './validated_range'; diff --git a/src/plugins/kibana_react/public/toolbar_button/__snapshots__/toolbar_button.test.tsx.snap b/src/plugins/kibana_react/public/toolbar_button/__snapshots__/toolbar_button.test.tsx.snap new file mode 100644 index 0000000000000..294be46398e8a --- /dev/null +++ b/src/plugins/kibana_react/public/toolbar_button/__snapshots__/toolbar_button.test.tsx.snap @@ -0,0 +1,199 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`font weights bold is applied 1`] = ` + +`; + +exports[`font weights normal is applied 1`] = ` + +`; + +exports[`hasArrow is rendered 1`] = ` + +`; + +exports[`positions center is applied 1`] = ` + +`; + +exports[`positions left is applied 1`] = ` + +`; + +exports[`positions none is applied 1`] = ` + +`; + +exports[`positions right is applied 1`] = ` + +`; + +exports[`sizes m is applied 1`] = ` + +`; + +exports[`sizes s is applied 1`] = ` + +`; diff --git a/src/plugins/kibana_react/public/toolbar_button/index.ts b/src/plugins/kibana_react/public/toolbar_button/index.ts new file mode 100644 index 0000000000000..f952741291b68 --- /dev/null +++ b/src/plugins/kibana_react/public/toolbar_button/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +export * from './toolbar_button'; diff --git a/x-pack/plugins/lens/public/shared_components/toolbar_button.scss b/src/plugins/kibana_react/public/toolbar_button/toolbar_button.scss similarity index 81% rename from x-pack/plugins/lens/public/shared_components/toolbar_button.scss rename to src/plugins/kibana_react/public/toolbar_button/toolbar_button.scss index 61b02f47678c3..f290b3c7c5f89 100644 --- a/x-pack/plugins/lens/public/shared_components/toolbar_button.scss +++ b/src/plugins/kibana_react/public/toolbar_button/toolbar_button.scss @@ -1,4 +1,4 @@ -.lnsToolbarButton { +.kbnToolbarButton { line-height: $euiButtonHeight; // Keeps alignment of text and chart icon background-color: $euiColorEmptyShade; @@ -15,11 +15,11 @@ pointer-events: initial; } - .lnsToolbarButton__text > svg { + .kbnToolbarButton__text > svg { margin-top: -1px; // Just some weird alignment issue when icon is the child not the `iconType` } - .lnsToolbarButton__text:empty { + .kbnToolbarButton__text:empty { margin: 0; } @@ -27,34 +27,33 @@ &[class*='fullWidth'] { text-align: left; - .lnsToolbarButton__content { + .kbnToolbarButton__content { justify-content: space-between; } } - } -.lnsToolbarButton--groupLeft { +.kbnToolbarButton--groupLeft { border-top-right-radius: 0; border-bottom-right-radius: 0; } -.lnsToolbarButton--groupCenter { +.kbnToolbarButton--groupCenter { border-radius: 0; border-left: none; } -.lnsToolbarButton--groupRight { +.kbnToolbarButton--groupRight { border-top-left-radius: 0; border-bottom-left-radius: 0; border-left: none; } -.lnsToolbarButton--bold { +.kbnToolbarButton--bold { font-weight: $euiFontWeightBold; } -.lnsToolbarButton--s { +.kbnToolbarButton--s { box-shadow: none !important; // sass-lint:disable-line no-important font-size: $euiFontSizeS; } diff --git a/src/plugins/kibana_react/public/toolbar_button/toolbar_button.test.tsx b/src/plugins/kibana_react/public/toolbar_button/toolbar_button.test.tsx new file mode 100644 index 0000000000000..3d4ce29ffa5e9 --- /dev/null +++ b/src/plugins/kibana_react/public/toolbar_button/toolbar_button.test.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; +import { ToolbarButton, POSITIONS, WEIGHTS, TOOLBAR_BUTTON_SIZES } from './toolbar_button'; + +const noop = () => {}; + +describe('sizes', () => { + TOOLBAR_BUTTON_SIZES.forEach((size) => { + test(`${size} is applied`, () => { + const component = shallow(); + expect(component).toMatchSnapshot(); + }); + }); +}); + +describe('positions', () => { + POSITIONS.forEach((position) => { + test(`${position} is applied`, () => { + const component = shallow(); + expect(component).toMatchSnapshot(); + }); + }); +}); + +describe('font weights', () => { + WEIGHTS.forEach((weight) => { + test(`${weight} is applied`, () => { + const component = shallow(); + expect(component).toMatchSnapshot(); + }); + }); +}); + +describe('hasArrow', () => { + it('is rendered', () => { + const component = shallow(); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/lens/public/shared_components/toolbar_button.tsx b/src/plugins/kibana_react/public/toolbar_button/toolbar_button.tsx similarity index 61% rename from x-pack/plugins/lens/public/shared_components/toolbar_button.tsx rename to src/plugins/kibana_react/public/toolbar_button/toolbar_button.tsx index 2ba227e6ff84f..388a11992268e 100644 --- a/x-pack/plugins/lens/public/shared_components/toolbar_button.tsx +++ b/src/plugins/kibana_react/public/toolbar_button/toolbar_button.tsx @@ -1,7 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. */ import './toolbar_button.scss'; @@ -11,16 +13,24 @@ import { EuiButton, PropsOf, EuiButtonProps } from '@elastic/eui'; const groupPositionToClassMap = { none: null, - left: 'lnsToolbarButton--groupLeft', - center: 'lnsToolbarButton--groupCenter', - right: 'lnsToolbarButton--groupRight', + left: 'toolbarButton--groupLeft', + center: 'toolbarButton--groupCenter', + right: 'toolbarButton--groupRight', }; +type ButtonPositions = keyof typeof groupPositionToClassMap; +export const POSITIONS = Object.keys(groupPositionToClassMap) as ButtonPositions[]; + +type Weights = 'normal' | 'bold'; +export const WEIGHTS = ['normal', 'bold'] as Weights[]; + +export const TOOLBAR_BUTTON_SIZES: Array = ['s', 'm']; + export type ToolbarButtonProps = PropsOf & { /** * Determines prominence */ - fontWeight?: 'normal' | 'bold'; + fontWeight?: Weights; /** * Smaller buttons also remove extra shadow for less prominence */ @@ -32,7 +42,7 @@ export type ToolbarButtonProps = PropsOf & { /** * Adjusts the borders for groupings */ - groupPosition?: 'none' | 'left' | 'center' | 'right'; + groupPosition?: ButtonPositions; dataTestSubj?: string; }; @@ -47,9 +57,9 @@ export const ToolbarButton: React.FunctionComponent = ({ ...rest }) => { const classes = classNames( - 'lnsToolbarButton', + 'kbnToolbarButton', groupPositionToClassMap[groupPosition], - [`lnsToolbarButton--${fontWeight}`, `lnsToolbarButton--${size}`], + [`kbnToolbarButton--${fontWeight}`, `kbnToolbarButton--${size}`], className ); return ( @@ -60,10 +70,10 @@ export const ToolbarButton: React.FunctionComponent = ({ iconType={hasArrow ? 'arrowDown' : ''} color="text" contentProps={{ - className: 'lnsToolbarButton__content', + className: 'kbnToolbarButton__content', }} textProps={{ - className: 'lnsToolbarButton__text', + className: 'kbnToolbarButton__text', }} {...rest} size={size} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx index bc537e5a7d689..d3ca72a02940d 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx @@ -9,7 +9,7 @@ import { EuiPopover, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { NativeRenderer } from '../../../native_renderer'; import { Visualization, VisualizationLayerWidgetProps } from '../../../types'; -import { ToolbarButton } from '../../../shared_components'; +import { ToolbarButton } from '../../../../../../../src/plugins/kibana_react/public'; export function LayerSettings({ layerId, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx index 659626149aef2..474720e638939 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx @@ -24,7 +24,7 @@ import { Visualization, FramePublicAPI, Datasource } from '../../../types'; import { Action } from '../state_management'; import { getSuggestions, switchToSuggestion, Suggestion } from '../suggestion_helpers'; import { trackUiEvent } from '../../../lens_ui_telemetry'; -import { ToolbarButton } from '../../../shared_components'; +import { ToolbarButton } from '../../../../../../../src/plugins/kibana_react/public'; interface VisualizationSelection { visualizationId: string; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx index 25cb34d19beb8..f2d72079c2185 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx @@ -9,7 +9,7 @@ import React, { useState } from 'react'; import { EuiPopover, EuiPopoverTitle, EuiSelectable, EuiSelectableProps } from '@elastic/eui'; import { IndexPatternRef } from './types'; import { trackUiEvent } from '../lens_ui_telemetry'; -import { ToolbarButtonProps, ToolbarButton } from '../shared_components'; +import { ToolbarButton, ToolbarButtonProps } from '../../../../../src/plugins/kibana_react/public'; export type ChangeIndexPatternTriggerProps = ToolbarButtonProps & { label: string; diff --git a/x-pack/plugins/lens/public/shared_components/index.ts b/x-pack/plugins/lens/public/shared_components/index.ts index 622bf5397c935..c4e79c3f1bcb8 100644 --- a/x-pack/plugins/lens/public/shared_components/index.ts +++ b/x-pack/plugins/lens/public/shared_components/index.ts @@ -6,6 +6,5 @@ export * from './empty_placeholder'; export { ToolbarPopoverProps, ToolbarPopover } from './toolbar_popover'; -export { ToolbarButtonProps, ToolbarButton } from './toolbar_button'; export { LegendSettingsPopover } from './legend_settings_popover'; export { PalettePicker } from './palette_picker'; diff --git a/x-pack/plugins/lens/public/shared_components/toolbar_popover.tsx b/x-pack/plugins/lens/public/shared_components/toolbar_popover.tsx index cf2268c6eadf2..adc6d082f514f 100644 --- a/x-pack/plugins/lens/public/shared_components/toolbar_popover.tsx +++ b/x-pack/plugins/lens/public/shared_components/toolbar_popover.tsx @@ -6,8 +6,8 @@ import React, { useState } from 'react'; import { EuiFlexItem, EuiPopover, EuiIcon, EuiPopoverTitle, IconType } from '@elastic/eui'; -import { ToolbarButton, ToolbarButtonProps } from './toolbar_button'; import { EuiIconLegend } from '../assets/legend'; +import { ToolbarButton, ToolbarButtonProps } from '../../../../../src/plugins/kibana_react/public'; const typeToIconMap: { [type: string]: string | IconType } = { legend: EuiIconLegend as IconType, diff --git a/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx b/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx index 45ec7098aa639..931e62ea1d13f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx @@ -16,12 +16,13 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { LayerConfig, AxesSettingsConfig } from './types'; -import { ToolbarPopover, ToolbarButtonProps } from '../shared_components'; +import { ToolbarPopover } from '../shared_components'; import { isHorizontalChart } from './state_helpers'; import { EuiIconAxisBottom } from '../assets/axis_bottom'; import { EuiIconAxisLeft } from '../assets/axis_left'; import { EuiIconAxisRight } from '../assets/axis_right'; import { EuiIconAxisTop } from '../assets/axis_top'; +import { ToolbarButtonProps } from '../../../../../src/plugins/kibana_react/public'; type AxesSettingsConfigKeys = keyof AxesSettingsConfig; export interface AxisSettingsPopoverProps { From 58e629ed7690feb11076a02eff461368cbe8ff32 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Mon, 25 Jan 2021 17:30:40 -0500 Subject: [PATCH 53/62] [CI] [TeamCity] Bump security_solution agent size to match other ciGroups, and build missing default plugin (#89241) --- .ci/teamcity/default/build.sh | 1 + .teamcity/src/builds/default/DefaultFunctionalBase.kt | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/.ci/teamcity/default/build.sh b/.ci/teamcity/default/build.sh index af90e24ef5fe8..140233f29e6af 100755 --- a/.ci/teamcity/default/build.sh +++ b/.ci/teamcity/default/build.sh @@ -14,6 +14,7 @@ node scripts/build_kibana_platform_plugins \ --scan-dir "$XPACK_DIR/test/plugin_api_integration/plugins" \ --scan-dir "$XPACK_DIR/test/plugin_api_perf/plugins" \ --scan-dir "$XPACK_DIR/test/licensing_plugin/plugins" \ + --scan-dir "$XPACK_DIR/test/usage_collection/plugins" \ --verbose tc_end_block "Build Platform Plugins" diff --git a/.teamcity/src/builds/default/DefaultFunctionalBase.kt b/.teamcity/src/builds/default/DefaultFunctionalBase.kt index d8124bd8521c0..dc2f7756efeb5 100644 --- a/.teamcity/src/builds/default/DefaultFunctionalBase.kt +++ b/.teamcity/src/builds/default/DefaultFunctionalBase.kt @@ -1,6 +1,8 @@ package builds.default +import StandardAgents import addTestSettings +import co.elastic.teamcity.common.requireAgent import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType open class DefaultFunctionalBase(init: BuildType.() -> Unit = {}) : BuildType({ @@ -8,6 +10,8 @@ open class DefaultFunctionalBase(init: BuildType.() -> Unit = {}) : BuildType({ param("env.KBN_NP_PLUGINS_BUILT", "true") } + requireAgent(StandardAgents["4"]!!) + dependencies { defaultBuildWithPlugins() } From b93b3a4fc19a3d7c43f3d9b26dc6ce43d1bca73c Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Mon, 25 Jan 2021 18:06:09 -0500 Subject: [PATCH 54/62] fix bad link (#89222) --- dev_docs/kibana_platform_plugin_intro.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_docs/kibana_platform_plugin_intro.mdx b/dev_docs/kibana_platform_plugin_intro.mdx index 412b8eb93b00f..3303561fae069 100644 --- a/dev_docs/kibana_platform_plugin_intro.mdx +++ b/dev_docs/kibana_platform_plugin_intro.mdx @@ -76,7 +76,7 @@ We recognize the need to better clarify the relationship between core functional The main difference between core functionality and functionality supplied by plugins, is in how it is accessed. Core is passed to plugins as the first parameter to their `start` and `setup` lifecycle functions, while plugin supplied functionality is passed as the second parameter. Plugin dependencies must be declared explicitly inside the `kibana.json` file. Core functionality is always provided. Read the -section on [how plugins interact with eachother and core](#how-plugins-interact-with-each-other-and-core) for more information. +section on for more information. ## The anatomy of a plugin From 0c9c2c91f5f51d29d355622c0c5c314433c63662 Mon Sep 17 00:00:00 2001 From: Spencer Date: Mon, 25 Jan 2021 16:30:18 -0700 Subject: [PATCH 55/62] [browserslist] remove unnecessary browsers (#89186) Co-authored-by: spalger --- .browserslistrc | 8 ++++---- .../basic_optimization.test.ts.snap | 2 +- yarn.lock | 18 ++++-------------- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/.browserslistrc b/.browserslistrc index 36298c0f8cb93..c54816e60aebe 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -2,10 +2,10 @@ last 2 Firefox versions last 2 Chrome versions last 2 Safari versions -> 0.25% -not ie 11 -not op_mini all -not samsung 4 +last 2 Edge versions +last 1 ios_saf versions +last 1 and_chr versions +last 1 samsung versions [dev] last 1 chrome versions diff --git a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap index 4681057ad0998..f23d8c9936980 100644 --- a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap +++ b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap @@ -108,4 +108,4 @@ exports[`prepares assets for distribution: baz bundle 1`] = ` exports[`prepares assets for distribution: foo async bundle 1`] = `"(window[\\"foo_bundle_jsonpfunction\\"]=window[\\"foo_bundle_jsonpfunction\\"]||[]).push([[1],{3:function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,\\"foo\\",(function(){return foo}));function foo(){}}}]);"`; -exports[`prepares assets for distribution: foo bundle 1`] = `"(function(modules){function webpackJsonpCallback(data){var chunkIds=data[0];var moreModules=data[1];var moduleId,chunkId,i=0,resolves=[];for(;i Date: Mon, 25 Jan 2021 21:01:50 -0500 Subject: [PATCH 56/62] [Monitoring] No longer overwriting time to the 1h default (#88102) * No longer overwriting time to the 1h default * Added auto refresh --- x-pack/plugins/monitoring/public/plugin.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/x-pack/plugins/monitoring/public/plugin.ts b/x-pack/plugins/monitoring/public/plugin.ts index a0de3a7663a12..52f8d07f4fdb6 100644 --- a/x-pack/plugins/monitoring/public/plugin.ts +++ b/x-pack/plugins/monitoring/public/plugin.ts @@ -18,7 +18,6 @@ import { FeatureCatalogueCategory, HomePublicPluginSetup, } from '../../../../src/plugins/home/public'; -import { UI_SETTINGS } from '../../../../src/plugins/data/public'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public'; import { MonitoringStartPluginDependencies, MonitoringConfig } from './types'; import { TriggersAndActionsUIPublicPluginSetup } from '../../triggers_actions_ui/public'; @@ -98,7 +97,6 @@ export class MonitoringPlugin }; this.setInitialTimefilter(deps); - const monitoringApp = new AngularApp(deps); const removeHistoryListener = params.history.listen((location) => { if (location.pathname === '' && location.hash === '') { @@ -121,18 +119,10 @@ export class MonitoringPlugin public stop() {} - private setInitialTimefilter({ core: coreContext, data }: MonitoringStartPluginDependencies) { + private setInitialTimefilter({ data }: MonitoringStartPluginDependencies) { const { timefilter } = data.query.timefilter; - const { uiSettings } = coreContext; const refreshInterval = { value: 10000, pause: false }; - const time = { from: 'now-1h', to: 'now' }; timefilter.setRefreshInterval(refreshInterval); - timefilter.setTime(time); - uiSettings.overrideLocalDefault( - UI_SETTINGS.TIMEPICKER_REFRESH_INTERVAL_DEFAULTS, - JSON.stringify(refreshInterval) - ); - uiSettings.overrideLocalDefault(UI_SETTINGS.TIMEPICKER_TIME_DEFAULTS, JSON.stringify(time)); } private getExternalConfig() { From 1750fc269c0b7784acd0c241b86618132e9a75e1 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 25 Jan 2021 21:10:05 -0500 Subject: [PATCH 57/62] [Monitoring] Fix newly added cloud link (#89067) * Fix cloud link * Update link * Fix tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/no_data/blurbs/cloud_deployment.js | 4 +++- .../exporters/__snapshots__/exporters.test.js.snap | 2 +- .../no_data/explanations/exporters/exporters.test.js | 10 ++++++++++ .../reasons/__snapshots__/reason_found.test.js.snap | 2 +- .../components/no_data/reasons/reason_found.test.js | 10 ++++++++++ 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/monitoring/public/components/no_data/blurbs/cloud_deployment.js b/x-pack/plugins/monitoring/public/components/no_data/blurbs/cloud_deployment.js index c01243fdeec47..e1262771f94a3 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/blurbs/cloud_deployment.js +++ b/x-pack/plugins/monitoring/public/components/no_data/blurbs/cloud_deployment.js @@ -7,8 +7,10 @@ import React from 'react'; import { EuiText, EuiTextColor, EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { Legacy } from '../../../legacy_shims'; export const CloudDeployment = () => { + const { ELASTIC_WEBSITE_URL } = Legacy.shims.docLinks; return ( @@ -32,7 +34,7 @@ export const CloudDeployment = () => { defaultMessage="section for a deployment to configure monitoring. For more information visit " /> the documentation page. diff --git a/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__snapshots__/exporters.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__snapshots__/exporters.test.js.snap index 99f5a979c812b..6bfc17121d511 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__snapshots__/exporters.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__snapshots__/exporters.test.js.snap @@ -135,7 +135,7 @@ Array [ section for a deployment to configure monitoring. For more information visit diff --git a/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/exporters.test.js b/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/exporters.test.js index 2bc581ffb1abb..280572b000011 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/exporters.test.js +++ b/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/exporters.test.js @@ -8,6 +8,16 @@ import React from 'react'; import { renderWithIntl } from '@kbn/test/jest'; import { ExplainExporters, ExplainExportersCloud } from './exporters'; +jest.mock('../../../../legacy_shims', () => ({ + Legacy: { + shims: { + docLinks: { + ELASTIC_WEBSITE_URL: 'https://www.elastic.co/', + }, + }, + }, +})); + describe('ExplainExporters', () => { test('should explain about xpack.monitoring.exporters setting', () => { const reason = { diff --git a/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/reason_found.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/reason_found.test.js.snap index e3d25f97c9f78..649d5f7f757ab 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/reason_found.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/reason_found.test.js.snap @@ -210,7 +210,7 @@ Array [ section for a deployment to configure monitoring. For more information visit diff --git a/x-pack/plugins/monitoring/public/components/no_data/reasons/reason_found.test.js b/x-pack/plugins/monitoring/public/components/no_data/reasons/reason_found.test.js index b4abda87ea1e0..b18351f22851e 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/reasons/reason_found.test.js +++ b/x-pack/plugins/monitoring/public/components/no_data/reasons/reason_found.test.js @@ -8,6 +8,16 @@ import React from 'react'; import { renderWithIntl } from '@kbn/test/jest'; import { ReasonFound } from '.'; +jest.mock('../../../legacy_shims', () => ({ + Legacy: { + shims: { + docLinks: { + ELASTIC_WEBSITE_URL: 'https://www.elastic.co/', + }, + }, + }, +})); + const enabler = {}; describe('ReasonFound', () => { From da72cd47b2d36ef39f22ae34594cbbe7533b7859 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 25 Jan 2021 21:11:32 -0500 Subject: [PATCH 58/62] Add toast for newly created alerts (#89202) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/alerts/lib/alerts_toast.tsx | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/x-pack/plugins/monitoring/public/alerts/lib/alerts_toast.tsx b/x-pack/plugins/monitoring/public/alerts/lib/alerts_toast.tsx index f478545046894..302a7f2fe469f 100644 --- a/x-pack/plugins/monitoring/public/alerts/lib/alerts_toast.tsx +++ b/x-pack/plugins/monitoring/public/alerts/lib/alerts_toast.tsx @@ -81,6 +81,24 @@ const showUnableToDisableWatcherClusterAlertsError = () => { }); }; +const showDisabledWatcherClusterAlertsError = () => { + Legacy.shims.toastNotifications.addWarning({ + title: toMountPoint( + + ), + text: toMountPoint( +

+ {i18n.translate('xpack.monitoring.healthCheck.disabledWatches.text', { + defaultMessage: `Review the alert definition using Setup mode and configure additional action connectors to get notified via your favorite method.`, + })} +

+ ), + }); +}; + export const showAlertsToast = (response: EnableAlertResponse) => { const { isSufficientlySecure, @@ -92,5 +110,7 @@ export const showAlertsToast = (response: EnableAlertResponse) => { showTlsAndEncryptionError(); } else if (disabledWatcherClusterAlerts === false) { showUnableToDisableWatcherClusterAlertsError(); + } else if (disabledWatcherClusterAlerts === true) { + showDisabledWatcherClusterAlertsError(); } }; From bc840b1c46a201bd158afeb8de2774ead5629947 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 26 Jan 2021 02:51:03 +0000 Subject: [PATCH 59/62] skip flaky suite (#89191) --- src/core/server/ui_settings/integration_tests/routes.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/server/ui_settings/integration_tests/routes.test.ts b/src/core/server/ui_settings/integration_tests/routes.test.ts index 89f5b6732a8fb..2f0a5ebc139e6 100644 --- a/src/core/server/ui_settings/integration_tests/routes.test.ts +++ b/src/core/server/ui_settings/integration_tests/routes.test.ts @@ -9,7 +9,8 @@ import { schema } from '@kbn/config-schema'; import * as kbnTestServer from '../../../test_helpers/kbn_server'; -describe('ui settings service', () => { +// FLAKY: https://github.com/elastic/kibana/issues/89191 +describe.skip('ui settings service', () => { describe('routes', () => { let root: ReturnType; beforeAll(async () => { From f5e869189b0bbf242637a2fb8f06c1e5e74633b7 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Tue, 26 Jan 2021 05:54:10 +0100 Subject: [PATCH 60/62] Improve documentation for index pattern custom label (#89137) Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> --- docs/management/index-patterns.asciidoc | 5 ++++- .../images/edit-field-format.png | Bin 45776 -> 99547 bytes docs/management/managing-fields.asciidoc | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) mode change 100755 => 100644 docs/management/index-patterns/images/edit-field-format.png diff --git a/docs/management/index-patterns.asciidoc b/docs/management/index-patterns.asciidoc index d83f2571ad26f..28dbacc628ce9 100644 --- a/docs/management/index-patterns.asciidoc +++ b/docs/management/index-patterns.asciidoc @@ -158,10 +158,13 @@ date values in {es}, you can use a {kib} field formatter to change the display t <>, and <>. +To customize the displayed field name provided by {es}, you can +use *Custom Label* . + A popularity counter keeps track of the fields you use most often. The top five most popular fields and their values are displayed in <>. -To edit the field format and popularity counter, click the edit icon +To edit the field display, click the edit icon (image:management/index-patterns/images/edit_icon.png[]) in the index pattern detail view. [role="screenshot"] diff --git a/docs/management/index-patterns/images/edit-field-format.png b/docs/management/index-patterns/images/edit-field-format.png old mode 100755 new mode 100644 index 15ab0c5bf876398f478f04594a31d3791cbca252..1ad29d82d2590dd124cdbcafbe0bc990d030100a GIT binary patch literal 99547 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 diff --git a/docs/management/managing-fields.asciidoc b/docs/management/managing-fields.asciidoc index 1b9d22699d359..97cd184a4db88 100644 --- a/docs/management/managing-fields.asciidoc +++ b/docs/management/managing-fields.asciidoc @@ -18,6 +18,7 @@ To format a field: . Open the main menu, and click *Stack Management > Index Patterns*. . Click the index pattern that contains the field you want to format. . Find the field you want to format and click the edit icon (image:management/index-patterns/images/edit_icon.png[]). +. Enter a custom label for the field, if needed. . Select a format and fill in the details. + [role="screenshot"] From ad953ee04eea83f27b89a6ef097d9253802a79f8 Mon Sep 17 00:00:00 2001 From: Daniil Date: Tue, 26 Jan 2021 10:57:09 +0300 Subject: [PATCH 61/62] [Visualizations] Changes in custom visualizations support (#88317) * Convert to typescript * Move related files directly into plugin * Implement toExpressionAst * Remove build_pipeline dedicated fn * Async import converter * Create a custom renderer * Move function directly into plugin * Update tests * Move files directly into related plugins * Remove ExprVis instance usage in maps visualizations * Use uiState updates * Fix minor issues * Update expression builder * Update styles * Create wrapper component * Update rendering * Create region map expression renderer * Fix tests and types * Fix initial render * Remove resize subscription * Fix custom visualization expression * Update region map expression in tests * Update files structure * Remove ReactVisController * Remove base visualization renderer * Remove extra vis properties * Use requiresSearch flag for agg based vis * Update types * Remove visualization expression function * Create toExpressionAst function * Update custom visualization example * Update interpreter functional tests * Enhance VisTypeDefinition interface * Enhance visualization types * Update license Co-authored-by: Alexey Antonov Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/editor/controls_tab.test.tsx | 2 - .../public/components/editor/controls_tab.tsx | 4 +- .../public/components/editor/index.tsx | 6 +- .../public/components/editor/options_tab.tsx | 4 +- .../public/input_control_vis_type.ts | 5 +- .../region_map/public/components/index.tsx | 4 +- .../public/components/region_map_options.tsx | 4 +- .../region_map/public/region_map_type.ts | 5 +- src/plugins/region_map/public/to_ast.ts | 4 +- .../public/components/tile_map_options.tsx | 4 +- src/plugins/tile_map/public/tile_map_type.ts | 5 +- src/plugins/tile_map/public/to_ast.ts | 4 +- .../components/agg_group_helper.test.ts | 2 - .../components/options/basic_options.tsx | 4 +- .../components/options/color_schema.tsx | 4 +- .../components/sidebar/use_option_tabs.ts | 5 +- .../vis_default_editor/public/index.ts | 1 - .../public/vis_options_props.tsx | 22 -- .../public/markdown_options.test.tsx | 4 +- .../public/markdown_options.tsx | 4 +- .../vis_type_markdown/public/markdown_vis.ts | 7 +- .../public/settings_options.tsx | 5 +- .../vis_type_markdown/public/to_ast.ts | 3 +- .../__snapshots__/metric_vis_fn.test.ts.snap | 3 - .../public/__snapshots__/to_ast.test.ts.snap | 9 +- .../public/components/metric_vis_options.tsx | 4 +- .../vis_type_metric/public/metric_vis_fn.ts | 4 - .../vis_type_metric/public/metric_vis_type.ts | 6 +- .../vis_type_metric/public/to_ast.test.ts | 23 +- src/plugins/vis_type_metric/public/to_ast.ts | 9 +- .../public/components/table_vis_options.tsx | 10 +- .../components/table_vis_options_lazy.tsx | 5 +- .../public/legacy/table_vis_legacy_type.ts | 5 +- .../vis_type_table/public/table_vis_type.ts | 8 +- src/plugins/vis_type_table/public/to_ast.ts | 4 +- .../public/components/tag_cloud_options.tsx | 5 +- .../public/tag_cloud_type.ts | 6 +- .../vis_type_tagcloud/public/to_ast.ts | 4 +- .../public/timelion_options.tsx | 4 +- .../public/timelion_vis_type.tsx | 6 +- .../public/components/vega_vis_editor.tsx | 4 +- .../components/vega_vis_editor_lazy.tsx | 4 +- src/plugins/vis_type_vega/public/plugin.ts | 2 +- src/plugins/vis_type_vega/public/vega_type.ts | 16 +- src/plugins/vis_type_vislib/public/area.ts | 9 +- .../public/editor/components/gauge/index.tsx | 6 +- .../editor/components/heatmap/index.tsx | 4 +- .../components/heatmap/labels_panel.tsx | 5 +- .../public/editor/components/index.tsx | 11 +- .../public/editor/components/pie.tsx | 5 +- src/plugins/vis_type_vislib/public/gauge.ts | 8 +- src/plugins/vis_type_vislib/public/goal.ts | 9 +- src/plugins/vis_type_vislib/public/heatmap.ts | 7 +- .../vis_type_vislib/public/histogram.ts | 9 +- .../vis_type_vislib/public/horizontal_bar.ts | 9 +- src/plugins/vis_type_vislib/public/line.ts | 9 +- src/plugins/vis_type_vislib/public/pie.ts | 6 +- src/plugins/vis_type_vislib/public/to_ast.ts | 22 +- .../vis_type_vislib/public/to_ast_esaggs.ts | 5 +- .../public/vis_type_vislib_vis_types.ts | 5 +- .../vis_type_xy/public/editor/collections.ts | 65 +++-- .../public/editor/common_config.tsx | 6 +- .../components/common/validation_wrapper.tsx | 6 +- .../metrics_axes/category_axis_panel.tsx | 9 +- .../public/sample_vis.test.mocks.ts | 8 - .../vis_type_xy/public/types/vis_type.ts | 9 +- .../vis_type_xy/public/vis_types/area.ts | 1 + .../vis_type_xy/public/vis_types/histogram.ts | 1 + .../public/vis_types/horizontal_bar.ts | 1 + .../vis_type_xy/public/vis_types/line.ts | 1 + .../visualization_requesterror.test.js.snap | 24 -- .../public/components/_visualization.scss | 42 --- .../visualizations/public/components/index.ts | 2 - .../public/components/visualization.test.js | 92 ------- .../public/components/visualization.tsx | 73 ----- .../components/visualization_chart.test.js | 56 ---- .../public/components/visualization_chart.tsx | 134 ---------- .../visualization_requesterror.test.js | 28 -- .../components/visualization_requesterror.tsx | 51 ---- .../public/embeddable/_embeddables.scss | 4 - .../public/embeddable/get_index_pattern.ts | 25 -- .../public/embeddable/to_ast.ts | 45 ++++ .../public/embeddable/visualize_embeddable.ts | 7 +- .../visualize_embeddable_factory.tsx | 4 +- .../visualizations/public/expressions/vis.ts | 157 ----------- .../expressions/visualization_function.ts | 144 ---------- .../expressions/visualization_renderer.tsx | 51 ---- src/plugins/visualizations/public/index.ts | 27 +- .../__snapshots__/build_pipeline.test.ts.snap | 3 - .../public/legacy/build_pipeline.test.ts | 86 ------ .../public/legacy/build_pipeline.ts | 253 ------------------ src/plugins/visualizations/public/mocks.ts | 1 - src/plugins/visualizations/public/plugin.ts | 10 - src/plugins/visualizations/public/services.ts | 18 +- src/plugins/visualizations/public/types.ts | 44 +-- src/plugins/visualizations/public/vis.ts | 7 +- .../visualizations/public/vis_schemas.ts | 136 ++++++++++ .../public/vis_types/base_vis_type.test.ts | 11 +- .../public/vis_types/base_vis_type.ts | 67 +---- .../visualizations/public/vis_types/index.ts | 5 +- .../public/vis_types/react_vis_controller.tsx | 46 ---- .../public/vis_types/react_vis_type.test.ts | 36 --- .../public/vis_types/react_vis_type.ts | 35 --- .../public/vis_types/schemas.ts | 1 - .../visualizations/public/vis_types/types.ts | 80 ++++-- .../public/vis_types/types_service.ts | 26 +- .../agg_based_selection.test.tsx | 24 +- .../agg_based_selection.tsx | 6 +- .../group_selection/group_selection.test.tsx | 36 ++- .../group_selection/group_selection.tsx | 10 +- .../public/wizard/new_vis_modal.test.tsx | 25 +- .../public/wizard/new_vis_modal.tsx | 12 +- .../search_selection/search_selection.tsx | 4 +- .../snapshots/baseline/combined_test3.json | 2 +- .../snapshots/baseline/final_output_test.json | 2 +- .../snapshots/baseline/metric_all_data.json | 2 +- .../baseline/metric_invalid_data.json | 2 +- .../baseline/metric_multi_metric_data.json | 2 +- .../baseline/metric_percentage_mode.json | 2 +- .../baseline/metric_single_metric_data.json | 2 +- .../snapshots/baseline/partial_test_2.json | 2 +- .../snapshots/baseline/step_output_test3.json | 2 +- .../snapshots/session/combined_test3.json | 2 +- .../snapshots/session/final_output_test.json | 2 +- .../snapshots/session/metric_all_data.json | 2 +- .../session/metric_invalid_data.json | 2 +- .../session/metric_multi_metric_data.json | 2 +- .../session/metric_percentage_mode.json | 2 +- .../session/metric_single_metric_data.json | 2 +- .../snapshots/session/partial_test_2.json | 2 +- .../snapshots/session/step_output_test3.json | 2 +- .../kbn_tp_custom_visualizations/kibana.json | 1 + .../public/plugin.ts | 29 +- .../self_changing_components.tsx | 9 +- .../self_changing_editor.tsx | 4 +- .../public/self_changing_vis_fn.ts | 66 +++++ .../public/self_changing_vis_renderer.tsx | 26 ++ .../public/to_ast.ts | 30 +++ .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 140 files changed, 751 insertions(+), 1889 deletions(-) delete mode 100644 src/plugins/vis_default_editor/public/vis_options_props.tsx delete mode 100644 src/plugins/visualizations/public/components/__snapshots__/visualization_requesterror.test.js.snap delete mode 100644 src/plugins/visualizations/public/components/visualization.test.js delete mode 100644 src/plugins/visualizations/public/components/visualization.tsx delete mode 100644 src/plugins/visualizations/public/components/visualization_chart.test.js delete mode 100644 src/plugins/visualizations/public/components/visualization_chart.tsx delete mode 100644 src/plugins/visualizations/public/components/visualization_requesterror.test.js delete mode 100644 src/plugins/visualizations/public/components/visualization_requesterror.tsx delete mode 100644 src/plugins/visualizations/public/embeddable/get_index_pattern.ts create mode 100644 src/plugins/visualizations/public/embeddable/to_ast.ts delete mode 100644 src/plugins/visualizations/public/expressions/vis.ts delete mode 100644 src/plugins/visualizations/public/expressions/visualization_function.ts delete mode 100644 src/plugins/visualizations/public/expressions/visualization_renderer.tsx delete mode 100644 src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap delete mode 100644 src/plugins/visualizations/public/legacy/build_pipeline.test.ts delete mode 100644 src/plugins/visualizations/public/legacy/build_pipeline.ts create mode 100644 src/plugins/visualizations/public/vis_schemas.ts delete mode 100644 src/plugins/visualizations/public/vis_types/react_vis_controller.tsx delete mode 100644 src/plugins/visualizations/public/vis_types/react_vis_type.test.ts delete mode 100644 src/plugins/visualizations/public/vis_types/react_vis_type.ts create mode 100644 test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis_fn.ts create mode 100644 test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis_renderer.tsx create mode 100644 test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/to_ast.ts diff --git a/src/plugins/input_control_vis/public/components/editor/controls_tab.test.tsx b/src/plugins/input_control_vis/public/components/editor/controls_tab.test.tsx index ec9b5ee7e43da..4d82571995d58 100644 --- a/src/plugins/input_control_vis/public/components/editor/controls_tab.test.tsx +++ b/src/plugins/input_control_vis/public/components/editor/controls_tab.test.tsx @@ -29,8 +29,6 @@ beforeEach(() => { name: 'test', title: 'test', visualization: null, - requestHandler: 'test', - responseHandler: 'test', stage: 'beta', requiresSearch: false, hidden: false, diff --git a/src/plugins/input_control_vis/public/components/editor/controls_tab.tsx b/src/plugins/input_control_vis/public/components/editor/controls_tab.tsx index a2ae1e6851a6b..5e432d81ebefb 100644 --- a/src/plugins/input_control_vis/public/components/editor/controls_tab.tsx +++ b/src/plugins/input_control_vis/public/components/editor/controls_tab.tsx @@ -19,7 +19,7 @@ import { EuiSelect, } from '@elastic/eui'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { IIndexPattern } from 'src/plugins/data/public'; import { ControlEditor } from './control_editor'; import { @@ -40,7 +40,7 @@ interface ControlsTabUiState { type: CONTROL_TYPES; } -export type ControlsTabProps = VisOptionsProps & { +export type ControlsTabProps = VisEditorOptionsProps & { deps: InputControlVisDependencies; }; diff --git a/src/plugins/input_control_vis/public/components/editor/index.tsx b/src/plugins/input_control_vis/public/components/editor/index.tsx index 15b1039a8cb3c..ee457a47a3b6d 100644 --- a/src/plugins/input_control_vis/public/components/editor/index.tsx +++ b/src/plugins/input_control_vis/public/components/editor/index.tsx @@ -7,7 +7,7 @@ */ import React, { lazy } from 'react'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { InputControlVisDependencies } from '../../plugin'; import { InputControlVisParams } from '../../types'; @@ -15,9 +15,9 @@ const ControlsTab = lazy(() => import('./controls_tab')); const OptionsTab = lazy(() => import('./options_tab')); export const getControlsTab = (deps: InputControlVisDependencies) => ( - props: VisOptionsProps + props: VisEditorOptionsProps ) => ; -export const OptionsTabLazy = (props: VisOptionsProps) => ( +export const OptionsTabLazy = (props: VisEditorOptionsProps) => ( ); diff --git a/src/plugins/input_control_vis/public/components/editor/options_tab.tsx b/src/plugins/input_control_vis/public/components/editor/options_tab.tsx index fa995be0840ce..9a7eaa71c3324 100644 --- a/src/plugins/input_control_vis/public/components/editor/options_tab.tsx +++ b/src/plugins/input_control_vis/public/components/editor/options_tab.tsx @@ -12,10 +12,10 @@ import { EuiForm, EuiFormRow, EuiSwitch } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiSwitchEvent } from '@elastic/eui'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { InputControlVisParams } from '../../types'; -export type OptionsTabProps = VisOptionsProps; +export type OptionsTabProps = VisEditorOptionsProps; class OptionsTab extends PureComponent { handleUpdateFiltersChange = (event: EuiSwitchEvent) => { diff --git a/src/plugins/input_control_vis/public/input_control_vis_type.ts b/src/plugins/input_control_vis/public/input_control_vis_type.ts index 8d8fdf2cfc568..00e680aa5d328 100644 --- a/src/plugins/input_control_vis/public/input_control_vis_type.ts +++ b/src/plugins/input_control_vis/public/input_control_vis_type.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import { VisGroups, BaseVisTypeOptions } from '../../visualizations/public'; +import { VisGroups, VisTypeDefinition } from '../../visualizations/public'; import { getControlsTab, OptionsTabLazy } from './components/editor'; import { InputControlVisDependencies } from './plugin'; import { toExpressionAst } from './to_ast'; @@ -15,7 +15,7 @@ import { InputControlVisParams } from './types'; export function createInputControlVisTypeDefinition( deps: InputControlVisDependencies -): BaseVisTypeOptions { +): VisTypeDefinition { const ControlsTab = getControlsTab(deps); return { @@ -56,7 +56,6 @@ export function createInputControlVisTypeDefinition( ], }, inspectorAdapters: {}, - requestHandler: 'none', toExpressionAst, }; } diff --git a/src/plugins/region_map/public/components/index.tsx b/src/plugins/region_map/public/components/index.tsx index 2ad3c0ffec3f4..187c36f9cc46d 100644 --- a/src/plugins/region_map/public/components/index.tsx +++ b/src/plugins/region_map/public/components/index.tsx @@ -8,11 +8,11 @@ import React, { lazy } from 'react'; import { IServiceSettings } from 'src/plugins/maps_legacy/public'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { RegionMapVisParams } from '../region_map_types'; const RegionMapOptions = lazy(() => import('./region_map_options')); export const createRegionMapOptions = (getServiceSettings: () => Promise) => ( - props: VisOptionsProps + props: VisEditorOptionsProps ) => ; diff --git a/src/plugins/region_map/public/components/region_map_options.tsx b/src/plugins/region_map/public/components/region_map_options.tsx index 0b11e1c5164d3..43bb15a547435 100644 --- a/src/plugins/region_map/public/components/region_map_options.tsx +++ b/src/plugins/region_map/public/components/region_map_options.tsx @@ -10,7 +10,7 @@ import React, { useCallback, useMemo } from 'react'; import { EuiIcon, EuiLink, EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { FileLayerField, VectorLayer, IServiceSettings } from '../../../maps_legacy/public'; import { SelectOption, SwitchOption, NumberInputOption } from '../../../vis_default_editor/public'; import { WmsOptions } from '../../../maps_legacy/public'; @@ -28,7 +28,7 @@ const mapFieldForOption = ({ description, name }: FileLayerField) => ({ export type RegionMapOptionsProps = { getServiceSettings: () => Promise; -} & VisOptionsProps; +} & VisEditorOptionsProps; function RegionMapOptions(props: RegionMapOptionsProps) { const { getServiceSettings, stateParams, vis, setValue } = props; diff --git a/src/plugins/region_map/public/region_map_type.ts b/src/plugins/region_map/public/region_map_type.ts index bda478389faa1..c1d46cd603f0b 100644 --- a/src/plugins/region_map/public/region_map_type.ts +++ b/src/plugins/region_map/public/region_map_type.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; -import { BaseVisTypeOptions } from '../../visualizations/public'; +import { VisTypeDefinition } from '../../visualizations/public'; import { truncatedColorSchemas } from '../../charts/public'; import { ORIGIN } from '../../maps_legacy/public'; @@ -23,7 +23,7 @@ export function createRegionMapTypeDefinition({ uiSettings, regionmapsConfig, getServiceSettings, -}: RegionMapVisualizationDependencies): BaseVisTypeOptions { +}: RegionMapVisualizationDependencies): VisTypeDefinition { return { name: 'region_map', getInfoMessage: getDeprecationMessage, @@ -139,5 +139,6 @@ provided base maps, or add your own. Darker colors represent higher values.', return vis; }, + requiresSearch: true, }; } diff --git a/src/plugins/region_map/public/to_ast.ts b/src/plugins/region_map/public/to_ast.ts index 7c81b8fa9ecbb..77aaf18f0f697 100644 --- a/src/plugins/region_map/public/to_ast.ts +++ b/src/plugins/region_map/public/to_ast.ts @@ -11,11 +11,11 @@ import { IndexPatternLoadExpressionFunctionDefinition, } from '../../data/public'; import { buildExpression, buildExpressionFunction } from '../../expressions/public'; -import { getVisSchemas, Vis, BuildPipelineParams } from '../../visualizations/public'; +import { getVisSchemas, VisToExpressionAst } from '../../visualizations/public'; import { RegionMapExpressionFunctionDefinition } from './region_map_fn'; import { RegionMapVisConfig, RegionMapVisParams } from './region_map_types'; -export const toExpressionAst = (vis: Vis, params: BuildPipelineParams) => { +export const toExpressionAst: VisToExpressionAst = (vis, params) => { const esaggs = buildExpressionFunction('esaggs', { index: buildExpression([ buildExpressionFunction('indexPatternLoad', { diff --git a/src/plugins/tile_map/public/components/tile_map_options.tsx b/src/plugins/tile_map/public/components/tile_map_options.tsx index c30d314d166bb..d6155dbfec877 100644 --- a/src/plugins/tile_map/public/components/tile_map_options.tsx +++ b/src/plugins/tile_map/public/components/tile_map_options.tsx @@ -10,8 +10,8 @@ import React, { useEffect } from 'react'; import { EuiPanel, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { - VisOptionsProps, BasicOptions, SelectOption, SwitchOption, @@ -21,7 +21,7 @@ import { WmsOptions } from '../../../maps_legacy/public'; import { TileMapVisParams } from '../types'; import { MapTypes } from '../utils/map_types'; -export type TileMapOptionsProps = VisOptionsProps; +export type TileMapOptionsProps = VisEditorOptionsProps; function TileMapOptions(props: TileMapOptionsProps) { const { stateParams, setValue, vis } = props; diff --git a/src/plugins/tile_map/public/tile_map_type.ts b/src/plugins/tile_map/public/tile_map_type.ts index 7356da0cf8bcb..ceb9c138f043c 100644 --- a/src/plugins/tile_map/public/tile_map_type.ts +++ b/src/plugins/tile_map/public/tile_map_type.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import { BaseVisTypeOptions } from 'src/plugins/visualizations/public'; +import { VisTypeDefinition } from 'src/plugins/visualizations/public'; import { truncatedColorSchemas } from '../../charts/public'; // @ts-expect-error @@ -21,7 +21,7 @@ import { MapTypes } from './utils/map_types'; export function createTileMapTypeDefinition( dependencies: TileMapVisualizationDependencies -): BaseVisTypeOptions { +): VisTypeDefinition { const { uiSettings, getServiceSettings } = dependencies; return { @@ -147,5 +147,6 @@ export function createTileMapTypeDefinition( } return vis; }, + requiresSearch: true, }; } diff --git a/src/plugins/tile_map/public/to_ast.ts b/src/plugins/tile_map/public/to_ast.ts index f5a964d257431..8130b7df25276 100644 --- a/src/plugins/tile_map/public/to_ast.ts +++ b/src/plugins/tile_map/public/to_ast.ts @@ -11,11 +11,11 @@ import { IndexPatternLoadExpressionFunctionDefinition, } from '../../data/public'; import { buildExpression, buildExpressionFunction } from '../../expressions/public'; -import { getVisSchemas, Vis, BuildPipelineParams } from '../../visualizations/public'; +import { getVisSchemas, VisToExpressionAst } from '../../visualizations/public'; import { TileMapExpressionFunctionDefinition } from './tile_map_fn'; import { TileMapVisConfig, TileMapVisParams } from './types'; -export const toExpressionAst = (vis: Vis, params: BuildPipelineParams) => { +export const toExpressionAst: VisToExpressionAst = (vis, params) => { const esaggs = buildExpressionFunction('esaggs', { index: buildExpression([ buildExpressionFunction('indexPatternLoad', { diff --git a/src/plugins/vis_default_editor/public/components/agg_group_helper.test.ts b/src/plugins/vis_default_editor/public/components/agg_group_helper.test.ts index 131edb4601546..fb41d610d2175 100644 --- a/src/plugins/vis_default_editor/public/components/agg_group_helper.test.ts +++ b/src/plugins/vis_default_editor/public/components/agg_group_helper.test.ts @@ -50,7 +50,6 @@ describe('DefaultEditorGroup helpers', () => { min: 0, max: 3, aggFilter: [], - editor: false, params: [], defaults: null, mustBeFirst: true, @@ -62,7 +61,6 @@ describe('DefaultEditorGroup helpers', () => { min: 2, max: 3, aggFilter: [], - editor: false, params: [], defaults: null, }, diff --git a/src/plugins/vis_default_editor/public/components/options/basic_options.tsx b/src/plugins/vis_default_editor/public/components/options/basic_options.tsx index 4edfd9982ffbd..85a94e1228a3e 100644 --- a/src/plugins/vis_default_editor/public/components/options/basic_options.tsx +++ b/src/plugins/vis_default_editor/public/components/options/basic_options.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from '../../vis_options_props'; +import { VisEditorOptionsProps } from '../../../../visualizations/public'; import { SwitchOption } from './switch'; import { SelectOption } from './select'; @@ -23,7 +23,7 @@ function BasicOptions({ stateParams, setValue, vis, -}: VisOptionsProps) { +}: VisEditorOptionsProps) { return ( <> ( interface ColorSchemaOptionsProps extends ColorSchemaParams { disabled?: boolean; colorSchemas: ColorSchema[]; - uiState: VisOptionsProps['uiState']; + uiState: VisEditorOptionsProps['uiState']; setValue: SetColorSchemaOptionsValue; showHelpText?: boolean; } diff --git a/src/plugins/vis_default_editor/public/components/sidebar/use_option_tabs.ts b/src/plugins/vis_default_editor/public/components/sidebar/use_option_tabs.ts index fbec23fd199f5..be0e4dd9a8c20 100644 --- a/src/plugins/vis_default_editor/public/components/sidebar/use_option_tabs.ts +++ b/src/plugins/vis_default_editor/public/components/sidebar/use_option_tabs.ts @@ -9,13 +9,12 @@ import { useCallback, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { Vis } from '../../../../visualizations/public'; +import { Vis, VisEditorOptionsProps } from '../../../../visualizations/public'; -import { VisOptionsProps } from '../../vis_options_props'; import { DefaultEditorDataTab, DefaultEditorDataTabProps } from './data_tab'; export interface OptionTab { - editor: React.ComponentType; + editor: React.ComponentType; name: string; title: string; isSelected?: boolean; diff --git a/src/plugins/vis_default_editor/public/index.ts b/src/plugins/vis_default_editor/public/index.ts index a4c94dd664968..9d315f1a94ce7 100644 --- a/src/plugins/vis_default_editor/public/index.ts +++ b/src/plugins/vis_default_editor/public/index.ts @@ -16,7 +16,6 @@ export { PalettePicker } from './components/controls/palette_picker'; export * from './components/options'; export { RangesParamEditor, RangeValues } from './components/controls/ranges'; export * from './editor_size'; -export * from './vis_options_props'; export * from './utils'; export const plugin = (context: PluginInitializerContext) => { diff --git a/src/plugins/vis_default_editor/public/vis_options_props.tsx b/src/plugins/vis_default_editor/public/vis_options_props.tsx deleted file mode 100644 index 6180686b6996d..0000000000000 --- a/src/plugins/vis_default_editor/public/vis_options_props.tsx +++ /dev/null @@ -1,22 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { Vis, PersistedState } from 'src/plugins/visualizations/public'; -import { IAggConfigs } from 'src/plugins/data/public'; - -export interface VisOptionsProps { - aggs: IAggConfigs; - hasHistogramAgg: boolean; - isTabSelected: boolean; - stateParams: VisParamType; - vis: Vis; - uiState: PersistedState; - setValue(paramName: T, value: VisParamType[T]): void; - setValidity(isValid: boolean): void; - setTouched(isTouched: boolean): void; -} diff --git a/src/plugins/vis_type_markdown/public/markdown_options.test.tsx b/src/plugins/vis_type_markdown/public/markdown_options.test.tsx index cf27118ac71e4..9d464cf9e2063 100644 --- a/src/plugins/vis_type_markdown/public/markdown_options.test.tsx +++ b/src/plugins/vis_type_markdown/public/markdown_options.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { MarkdownVisParams } from './types'; import { MarkdownOptions } from './markdown_options'; @@ -21,7 +21,7 @@ describe('MarkdownOptions', () => { openLinksInNewTab: false, }, setValue: jest.fn(), - } as unknown) as VisOptionsProps; + } as unknown) as VisEditorOptionsProps; it('should match snapshot', () => { const comp = shallow(); diff --git a/src/plugins/vis_type_markdown/public/markdown_options.tsx b/src/plugins/vis_type_markdown/public/markdown_options.tsx index 72e4aaf8758b2..79d850b8c1fd8 100644 --- a/src/plugins/vis_type_markdown/public/markdown_options.tsx +++ b/src/plugins/vis_type_markdown/public/markdown_options.tsx @@ -18,10 +18,10 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { MarkdownVisParams } from './types'; -function MarkdownOptions({ stateParams, setValue }: VisOptionsProps) { +function MarkdownOptions({ stateParams, setValue }: VisEditorOptionsProps) { const onMarkdownUpdate = useCallback( ({ target: { value } }: React.ChangeEvent) => setValue('markdown', value), [setValue] diff --git a/src/plugins/vis_type_markdown/public/markdown_vis.ts b/src/plugins/vis_type_markdown/public/markdown_vis.ts index e698316dc8e70..adfdb811cc28c 100644 --- a/src/plugins/vis_type_markdown/public/markdown_vis.ts +++ b/src/plugins/vis_type_markdown/public/markdown_vis.ts @@ -11,10 +11,11 @@ import { i18n } from '@kbn/i18n'; import { MarkdownOptions } from './markdown_options'; import { SettingsOptions } from './settings_options_lazy'; import { DefaultEditorSize } from '../../vis_default_editor/public'; -import { VisGroups } from '../../visualizations/public'; +import { VisGroups, VisTypeDefinition } from '../../visualizations/public'; import { toExpressionAst } from './to_ast'; +import { MarkdownVisParams } from './types'; -export const markdownVisDefinition = { +export const markdownVisDefinition: VisTypeDefinition = { name: 'markdown', title: 'Markdown', isAccessible: true, @@ -58,7 +59,5 @@ export const markdownVisDefinition = { showTimePicker: false, showFilterBar: false, }, - requestHandler: 'none', - responseHandler: 'none', inspectorAdapters: {}, }; diff --git a/src/plugins/vis_type_markdown/public/settings_options.tsx b/src/plugins/vis_type_markdown/public/settings_options.tsx index 4ebcc88a8c04f..c18f1305d778a 100644 --- a/src/plugins/vis_type_markdown/public/settings_options.tsx +++ b/src/plugins/vis_type_markdown/public/settings_options.tsx @@ -10,10 +10,11 @@ import React from 'react'; import { EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps, SwitchOption, RangeOption } from '../../vis_default_editor/public'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; +import { SwitchOption, RangeOption } from '../../vis_default_editor/public'; import { MarkdownVisParams } from './types'; -function SettingsOptions({ stateParams, setValue }: VisOptionsProps) { +function SettingsOptions({ stateParams, setValue }: VisEditorOptionsProps) { return ( { +export const toExpressionAst: VisToExpressionAst = (vis) => { const { markdown, fontSize, openLinksInNewTab } = vis.params; const markdownVis = buildExpressionFunction( diff --git a/src/plugins/vis_type_metric/public/__snapshots__/metric_vis_fn.test.ts.snap b/src/plugins/vis_type_metric/public/__snapshots__/metric_vis_fn.test.ts.snap index fd8f3a712d8ae..a212cec2d8827 100644 --- a/src/plugins/vis_type_metric/public/__snapshots__/metric_vis_fn.test.ts.snap +++ b/src/plugins/vis_type_metric/public/__snapshots__/metric_vis_fn.test.ts.snap @@ -5,9 +5,6 @@ Object { "as": "metric_vis", "type": "render", "value": Object { - "params": Object { - "listenOnChange": true, - }, "visConfig": Object { "dimensions": Object { "metrics": undefined, diff --git a/src/plugins/vis_type_metric/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_type_metric/public/__snapshots__/to_ast.test.ts.snap index a89f41737d7bc..46e86c4c25de1 100644 --- a/src/plugins/vis_type_metric/public/__snapshots__/to_ast.test.ts.snap +++ b/src/plugins/vis_type_metric/public/__snapshots__/to_ast.test.ts.snap @@ -37,6 +37,9 @@ Object { "percentageMode": Array [ true, ], + "showLabels": Array [ + false, + ], }, "function": "metricVis", "type": "function", @@ -79,7 +82,11 @@ Object { "type": "function", }, Object { - "arguments": Object {}, + "arguments": Object { + "showLabels": Array [ + false, + ], + }, "function": "metricVis", "type": "function", }, diff --git a/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx b/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx index c8448ae68c074..069690b58e2a0 100644 --- a/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx +++ b/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx @@ -18,10 +18,10 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { ColorRanges, SetColorRangeValue, - VisOptionsProps, SwitchOption, SetColorSchemaOptionsValue, ColorSchemaOptions, @@ -37,7 +37,7 @@ function MetricVisOptions({ setTouched, vis, uiState, -}: VisOptionsProps) { +}: VisEditorOptionsProps) { const setMetricValue: ( paramName: T, value: MetricVisParam[T] diff --git a/src/plugins/vis_type_metric/public/metric_vis_fn.ts b/src/plugins/vis_type_metric/public/metric_vis_fn.ts index 892523493fb78..083d0413161b2 100644 --- a/src/plugins/vis_type_metric/public/metric_vis_fn.ts +++ b/src/plugins/vis_type_metric/public/metric_vis_fn.ts @@ -39,7 +39,6 @@ export interface MetricVisRenderValue { visType: typeof visType; visData: Input; visConfig: Pick; - params: any; } export type MetricVisExpressionFunctionDefinition = ExpressionFunctionDefinition< @@ -194,9 +193,6 @@ export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({ }, dimensions, }, - params: { - listenOnChange: true, - }, }, }; }, diff --git a/src/plugins/vis_type_metric/public/metric_vis_type.ts b/src/plugins/vis_type_metric/public/metric_vis_type.ts index cd4357574950d..e81d8f071c4f0 100644 --- a/src/plugins/vis_type_metric/public/metric_vis_type.ts +++ b/src/plugins/vis_type_metric/public/metric_vis_type.ts @@ -9,11 +9,12 @@ import { i18n } from '@kbn/i18n'; import { MetricVisOptions } from './components/metric_vis_options'; import { ColorSchemas, colorSchemas, ColorMode } from '../../charts/public'; -import { BaseVisTypeOptions } from '../../visualizations/public'; +import { VisTypeDefinition } from '../../visualizations/public'; import { AggGroupNames } from '../../data/public'; import { toExpressionAst } from './to_ast'; +import { VisParams } from './types'; -export const createMetricVisTypeDefinition = (): BaseVisTypeOptions => ({ +export const createMetricVisTypeDefinition = (): VisTypeDefinition => ({ name: 'metric', title: i18n.translate('visTypeMetric.metricTitle', { defaultMessage: 'Metric' }), icon: 'visMetric', @@ -110,4 +111,5 @@ export const createMetricVisTypeDefinition = (): BaseVisTypeOptions => ({ }, ], }, + requiresSearch: true, }); diff --git a/src/plugins/vis_type_metric/public/to_ast.test.ts b/src/plugins/vis_type_metric/public/to_ast.test.ts index fb90b27391c83..871b2ca0dc379 100644 --- a/src/plugins/vis_type_metric/public/to_ast.test.ts +++ b/src/plugins/vis_type_metric/public/to_ast.test.ts @@ -6,14 +6,17 @@ * Public License, v 1. */ +import { TimefilterContract } from 'src/plugins/data/public'; +import { Vis } from 'src/plugins/visualizations/public'; + import { toExpressionAst } from './to_ast'; -import { Vis } from '../../visualizations/public'; +import { VisParams } from './types'; describe('metric vis toExpressionAst function', () => { - let vis: Vis; + let vis: Vis; beforeEach(() => { - vis = { + vis = ({ isHierarchical: () => false, type: {}, params: { @@ -26,18 +29,22 @@ describe('metric vis toExpressionAst function', () => { aggs: [], } as any, }, - } as any; + } as unknown) as Vis; }); it('without params', () => { - vis.params = { metric: {} }; - const actual = toExpressionAst(vis, {}); + vis.params = { metric: {} } as VisParams; + const actual = toExpressionAst(vis, { + timefilter: {} as TimefilterContract, + }); expect(actual).toMatchSnapshot(); }); it('with percentage mode should have percentage format', () => { - vis.params = { metric: { percentageMode: true } }; - const actual = toExpressionAst(vis, {}); + vis.params = { metric: { percentageMode: true } } as VisParams; + const actual = toExpressionAst(vis, { + timefilter: {} as TimefilterContract, + }); expect(actual).toMatchSnapshot(); }); }); diff --git a/src/plugins/vis_type_metric/public/to_ast.ts b/src/plugins/vis_type_metric/public/to_ast.ts index 3b95b0c841016..3addce7610f2e 100644 --- a/src/plugins/vis_type_metric/public/to_ast.ts +++ b/src/plugins/vis_type_metric/public/to_ast.ts @@ -7,13 +7,14 @@ */ import { get } from 'lodash'; -import { getVisSchemas, SchemaConfig, Vis } from '../../visualizations/public'; +import { getVisSchemas, SchemaConfig, VisToExpressionAst } from '../../visualizations/public'; import { buildExpression, buildExpressionFunction } from '../../expressions/public'; import { MetricVisExpressionFunctionDefinition } from './metric_vis_fn'; import { EsaggsExpressionFunctionDefinition, IndexPatternLoadExpressionFunctionDefinition, } from '../../data/public'; +import { VisParams } from './types'; const prepareDimension = (params: SchemaConfig) => { const visdimension = buildExpressionFunction('visdimension', { accessor: params.accessor }); @@ -26,7 +27,7 @@ const prepareDimension = (params: SchemaConfig) => { return buildExpression([visdimension]); }; -export const toExpressionAst = (vis: Vis, params: any) => { +export const toExpressionAst: VisToExpressionAst = (vis, params) => { const esaggs = buildExpressionFunction('esaggs', { index: buildExpression([ buildExpressionFunction('indexPatternLoad', { @@ -34,7 +35,7 @@ export const toExpressionAst = (vis: Vis, params: any) => { }), ]), metricsAtAllLevels: vis.isHierarchical(), - partialRows: vis.params.showPartialRows || false, + partialRows: false, aggs: vis.data.aggs!.aggs.map((agg) => buildExpression(agg.toExpressionAst())), }); @@ -65,7 +66,7 @@ export const toExpressionAst = (vis: Vis, params: any) => { colorMode: metricColorMode, useRanges, invertColors, - showLabels: labels && labels.show, + showLabels: labels?.show ?? false, }); if (style) { diff --git a/src/plugins/vis_type_table/public/components/table_vis_options.tsx b/src/plugins/vis_type_table/public/components/table_vis_options.tsx index a70ecb43f1be7..163dae7ddc1c6 100644 --- a/src/plugins/vis_type_table/public/components/table_vis_options.tsx +++ b/src/plugins/vis_type_table/public/components/table_vis_options.tsx @@ -12,13 +12,9 @@ import { EuiIconTip, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { search } from '../../../data/public'; -import { - SwitchOption, - SelectOption, - NumberInputOption, - VisOptionsProps, -} from '../../../vis_default_editor/public'; +import { SwitchOption, SelectOption, NumberInputOption } from '../../../vis_default_editor/public'; import { TableVisParams } from '../../common'; import { totalAggregations } from './utils'; @@ -29,7 +25,7 @@ function TableOptions({ stateParams, setValidity, setValue, -}: VisOptionsProps) { +}: VisEditorOptionsProps) { const percentageColumns = useMemo( () => [ { diff --git a/src/plugins/vis_type_table/public/components/table_vis_options_lazy.tsx b/src/plugins/vis_type_table/public/components/table_vis_options_lazy.tsx index 716b77e9c91d2..2f7369996634a 100644 --- a/src/plugins/vis_type_table/public/components/table_vis_options_lazy.tsx +++ b/src/plugins/vis_type_table/public/components/table_vis_options_lazy.tsx @@ -8,12 +8,13 @@ import React, { lazy, Suspense } from 'react'; import { EuiLoadingSpinner } from '@elastic/eui'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; + +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { TableVisParams } from '../../common'; const TableOptionsComponent = lazy(() => import('./table_vis_options')); -export const TableOptions = (props: VisOptionsProps) => ( +export const TableOptions = (props: VisEditorOptionsProps) => ( }> diff --git a/src/plugins/vis_type_table/public/legacy/table_vis_legacy_type.ts b/src/plugins/vis_type_table/public/legacy/table_vis_legacy_type.ts index 3e1140275593d..53ad67ab4dd94 100644 --- a/src/plugins/vis_type_table/public/legacy/table_vis_legacy_type.ts +++ b/src/plugins/vis_type_table/public/legacy/table_vis_legacy_type.ts @@ -8,14 +8,14 @@ import { i18n } from '@kbn/i18n'; import { AggGroupNames } from '../../../data/public'; -import { BaseVisTypeOptions } from '../../../visualizations/public'; +import { VisTypeDefinition } from '../../../visualizations/public'; import { TableOptions } from '../components/table_vis_options_lazy'; import { VIS_EVENT_TO_TRIGGER } from '../../../visualizations/public'; import { TableVisParams, VIS_TYPE_TABLE } from '../../common'; import { toExpressionAst } from '../to_ast'; -export const tableVisLegacyTypeDefinition: BaseVisTypeOptions = { +export const tableVisLegacyTypeDefinition: VisTypeDefinition = { name: VIS_TYPE_TABLE, title: i18n.translate('visTypeTable.tableVisTitle', { defaultMessage: 'Data table', @@ -81,4 +81,5 @@ export const tableVisLegacyTypeDefinition: BaseVisTypeOptions = }, toExpressionAst, hierarchicalData: (vis) => vis.params.showPartialRows || vis.params.showMetricsAtAllLevels, + requiresSearch: true, }; diff --git a/src/plugins/vis_type_table/public/table_vis_type.ts b/src/plugins/vis_type_table/public/table_vis_type.ts index ef6d85db103b3..92148a5f085b0 100644 --- a/src/plugins/vis_type_table/public/table_vis_type.ts +++ b/src/plugins/vis_type_table/public/table_vis_type.ts @@ -7,15 +7,14 @@ */ import { i18n } from '@kbn/i18n'; -import { AggGroupNames } from '../../data/public'; -import { BaseVisTypeOptions } from '../../visualizations/public'; +import { AggGroupNames } from '../../data/public'; +import { VIS_EVENT_TO_TRIGGER, VisTypeDefinition } from '../../visualizations/public'; import { TableVisParams, VIS_TYPE_TABLE } from '../common'; import { TableOptions } from './components/table_vis_options_lazy'; -import { VIS_EVENT_TO_TRIGGER } from '../../../plugins/visualizations/public'; import { toExpressionAst } from './to_ast'; -export const tableVisTypeDefinition: BaseVisTypeOptions = { +export const tableVisTypeDefinition: VisTypeDefinition = { name: VIS_TYPE_TABLE, title: i18n.translate('visTypeTable.tableVisTitle', { defaultMessage: 'Data table', @@ -78,4 +77,5 @@ export const tableVisTypeDefinition: BaseVisTypeOptions = { }, toExpressionAst, hierarchicalData: (vis) => vis.params.showPartialRows || vis.params.showMetricsAtAllLevels, + requiresSearch: true, }; diff --git a/src/plugins/vis_type_table/public/to_ast.ts b/src/plugins/vis_type_table/public/to_ast.ts index 1cbe9832e4c98..ba79d82947d06 100644 --- a/src/plugins/vis_type_table/public/to_ast.ts +++ b/src/plugins/vis_type_table/public/to_ast.ts @@ -11,7 +11,7 @@ import { IndexPatternLoadExpressionFunctionDefinition, } from '../../data/public'; import { buildExpression, buildExpressionFunction } from '../../expressions/public'; -import { getVisSchemas, Vis, BuildPipelineParams } from '../../visualizations/public'; +import { getVisSchemas, VisToExpressionAst } from '../../visualizations/public'; import { TableVisParams } from '../common'; import { TableExpressionFunctionDefinition } from './table_vis_fn'; import { TableVisConfig } from './types'; @@ -41,7 +41,7 @@ const buildTableVisConfig = ( return visConfig; }; -export const toExpressionAst = (vis: Vis, params: BuildPipelineParams) => { +export const toExpressionAst: VisToExpressionAst = (vis, params) => { const esaggs = buildExpressionFunction('esaggs', { index: buildExpression([ buildExpressionFunction('indexPatternLoad', { diff --git a/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx b/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx index 2bcae41fae566..d15772b3fdb18 100644 --- a/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx +++ b/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx @@ -9,11 +9,12 @@ import React from 'react'; import { EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps, SelectOption, SwitchOption } from '../../../vis_default_editor/public'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; +import { SelectOption, SwitchOption } from '../../../vis_default_editor/public'; import { ValidatedDualRange } from '../../../kibana_react/public'; import { TagCloudVisParams } from '../types'; -function TagCloudOptions({ stateParams, setValue, vis }: VisOptionsProps) { +function TagCloudOptions({ stateParams, setValue, vis }: VisEditorOptionsProps) { const handleFontSizeChange = ([minFontSize, maxFontSize]: [string | number, string | number]) => { setValue('minFontSize', Number(minFontSize)); setValue('maxFontSize', Number(maxFontSize)); diff --git a/src/plugins/vis_type_tagcloud/public/tag_cloud_type.ts b/src/plugins/vis_type_tagcloud/public/tag_cloud_type.ts index 8159192b3491a..ebfa8db41944c 100644 --- a/src/plugins/vis_type_tagcloud/public/tag_cloud_type.ts +++ b/src/plugins/vis_type_tagcloud/public/tag_cloud_type.ts @@ -7,6 +7,7 @@ */ import { i18n } from '@kbn/i18n'; +import { AggGroupNames } from '../../data/public'; import { VIS_EVENT_TO_TRIGGER } from '../../visualizations/public'; import { TagCloudOptions } from './components/tag_cloud_options'; @@ -78,7 +79,7 @@ export const tagCloudVisTypeDefinition = { optionsTemplate: TagCloudOptions, schemas: [ { - group: 'metrics', + group: AggGroupNames.Metrics, name: 'metric', title: i18n.translate('visTypeTagCloud.vis.schemas.metricTitle', { defaultMessage: 'Tag size', @@ -96,7 +97,7 @@ export const tagCloudVisTypeDefinition = { defaults: [{ schema: 'metric', type: 'count' }], }, { - group: 'buckets', + group: AggGroupNames.Buckets, name: 'segment', title: i18n.translate('visTypeTagCloud.vis.schemas.segmentTitle', { defaultMessage: 'Tags', @@ -107,4 +108,5 @@ export const tagCloudVisTypeDefinition = { }, ], }, + requiresSearch: true, }; diff --git a/src/plugins/vis_type_tagcloud/public/to_ast.ts b/src/plugins/vis_type_tagcloud/public/to_ast.ts index adde82dd0dda9..1d9b24235b7ff 100644 --- a/src/plugins/vis_type_tagcloud/public/to_ast.ts +++ b/src/plugins/vis_type_tagcloud/public/to_ast.ts @@ -11,7 +11,7 @@ import { IndexPatternLoadExpressionFunctionDefinition, } from '../../data/public'; import { buildExpression, buildExpressionFunction } from '../../expressions/public'; -import { getVisSchemas, SchemaConfig, Vis, BuildPipelineParams } from '../../visualizations/public'; +import { getVisSchemas, SchemaConfig, VisToExpressionAst } from '../../visualizations/public'; import { TagcloudExpressionFunctionDefinition } from './tag_cloud_fn'; import { TagCloudVisParams } from './types'; @@ -26,7 +26,7 @@ const prepareDimension = (params: SchemaConfig) => { return buildExpression([visdimension]); }; -export const toExpressionAst = (vis: Vis, params: BuildPipelineParams) => { +export const toExpressionAst: VisToExpressionAst = (vis, params) => { const esaggs = buildExpressionFunction('esaggs', { index: buildExpression([ buildExpressionFunction('indexPatternLoad', { diff --git a/src/plugins/vis_type_timelion/public/timelion_options.tsx b/src/plugins/vis_type_timelion/public/timelion_options.tsx index 4627a5d5c8657..e366ec98f4be0 100644 --- a/src/plugins/vis_type_timelion/public/timelion_options.tsx +++ b/src/plugins/vis_type_timelion/public/timelion_options.tsx @@ -9,7 +9,7 @@ import React, { useCallback } from 'react'; import { EuiPanel } from '@elastic/eui'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { KibanaContextProvider } from '../../kibana_react/public'; import { TimelionVisParams } from './timelion_vis_fn'; @@ -18,7 +18,7 @@ import { TimelionVisDependencies } from './plugin'; import './timelion_options.scss'; -export type TimelionOptionsProps = VisOptionsProps; +export type TimelionOptionsProps = VisEditorOptionsProps; function TimelionOptions({ services, diff --git a/src/plugins/vis_type_timelion/public/timelion_vis_type.tsx b/src/plugins/vis_type_timelion/public/timelion_vis_type.tsx index 21344fc794c98..c9780ec6bcd1c 100644 --- a/src/plugins/vis_type_timelion/public/timelion_vis_type.tsx +++ b/src/plugins/vis_type_timelion/public/timelion_vis_type.tsx @@ -10,7 +10,6 @@ import React, { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { DefaultEditorSize } from '../../vis_default_editor/public'; -import { getTimelionRequestHandler } from './helpers/timelion_request_handler'; import { TimelionOptionsProps } from './timelion_options'; import { TimelionVisDependencies } from './plugin'; import { toExpressionAst } from './to_ast'; @@ -22,8 +21,6 @@ const TimelionOptions = lazy(() => import('./timelion_options')); export const TIMELION_VIS_NAME = 'timelion'; export function getTimelionVisDefinition(dependencies: TimelionVisDependencies) { - const timelionRequestHandler = getTimelionRequestHandler(dependencies); - // return the visType object, which kibana will use to display and configure new // Vis object of this type. return { @@ -45,9 +42,7 @@ export function getTimelionVisDefinition(dependencies: TimelionVisDependencies) ), defaultSize: DefaultEditorSize.MEDIUM, }, - requestHandler: timelionRequestHandler, toExpressionAst, - responseHandler: 'none', inspectorAdapters: {}, getSupportedTriggers: () => { return [VIS_EVENT_TO_TRIGGER.applyFilter]; @@ -57,5 +52,6 @@ export function getTimelionVisDefinition(dependencies: TimelionVisDependencies) showQueryBar: false, showFilterBar: false, }, + requiresSearch: true, }; } diff --git a/src/plugins/vis_type_vega/public/components/vega_vis_editor.tsx b/src/plugins/vis_type_vega/public/components/vega_vis_editor.tsx index 7afc01adec385..0a54a74b6b68c 100644 --- a/src/plugins/vis_type_vega/public/components/vega_vis_editor.tsx +++ b/src/plugins/vis_type_vega/public/components/vega_vis_editor.tsx @@ -13,7 +13,7 @@ import hjson from 'hjson'; import 'brace/mode/hjson'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { getNotifications } from '../services'; import { VisParams } from '../vega_fn'; import { VegaHelpMenu } from './vega_help_menu'; @@ -55,7 +55,7 @@ function format( } } -function VegaVisEditor({ stateParams, setValue }: VisOptionsProps) { +function VegaVisEditor({ stateParams, setValue }: VisEditorOptionsProps) { const onChange = useCallback( (value: string) => { setValue('spec', value); diff --git a/src/plugins/vis_type_vega/public/components/vega_vis_editor_lazy.tsx b/src/plugins/vis_type_vega/public/components/vega_vis_editor_lazy.tsx index 875f13453cf6d..bd26424552038 100644 --- a/src/plugins/vis_type_vega/public/components/vega_vis_editor_lazy.tsx +++ b/src/plugins/vis_type_vega/public/components/vega_vis_editor_lazy.tsx @@ -8,11 +8,11 @@ import React, { lazy } from 'react'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { VisParams } from '../vega_fn'; const VegaVisEditor = lazy(() => import('./vega_vis_editor')); -export const VegaVisEditorComponent = (props: VisOptionsProps) => ( +export const VegaVisEditorComponent = (props: VisEditorOptionsProps) => ( ); diff --git a/src/plugins/vis_type_vega/public/plugin.ts b/src/plugins/vis_type_vega/public/plugin.ts index b74b686500e6d..376ef84de23c3 100644 --- a/src/plugins/vis_type_vega/public/plugin.ts +++ b/src/plugins/vis_type_vega/public/plugin.ts @@ -84,7 +84,7 @@ export class VegaPlugin implements Plugin, void> { expressions.registerFunction(() => createVegaFn(visualizationDependencies)); expressions.registerRenderer(getVegaVisRenderer(visualizationDependencies)); - visualizations.createBaseVisualization(createVegaTypeDefinition(visualizationDependencies)); + visualizations.createBaseVisualization(createVegaTypeDefinition()); } public start(core: CoreStart, { data }: VegaPluginStartDependencies) { diff --git a/src/plugins/vis_type_vega/public/vega_type.ts b/src/plugins/vis_type_vega/public/vega_type.ts index 66e4b8b98056f..4b2ec65850030 100644 --- a/src/plugins/vis_type_vega/public/vega_type.ts +++ b/src/plugins/vis_type_vega/public/vega_type.ts @@ -8,16 +8,13 @@ import { i18n } from '@kbn/i18n'; import { parse } from 'hjson'; -import type { BaseVisTypeOptions } from 'src/plugins/visualizations/public'; import { DefaultEditorSize } from '../../vis_default_editor/public'; -import type { VegaVisualizationDependencies } from './plugin'; +import { VIS_EVENT_TO_TRIGGER, VisGroups, VisTypeDefinition } from '../../visualizations/public'; -import { createVegaRequestHandler } from './vega_request_handler'; import { getDefaultSpec } from './default_spec'; import { extractIndexPatternsFromSpec } from './lib/extract_index_pattern'; import { createInspectorAdapters } from './vega_inspector'; -import { VIS_EVENT_TO_TRIGGER, VisGroups } from '../../visualizations/public'; import { toExpressionAst } from './to_ast'; import { getInfoMessage } from './components/experimental_map_vis_info'; import { VegaVisEditorComponent } from './components/vega_vis_editor_lazy'; @@ -25,11 +22,7 @@ import { VegaVisEditorComponent } from './components/vega_vis_editor_lazy'; import type { VegaSpec } from './data_model/types'; import type { VisParams } from './vega_fn'; -export const createVegaTypeDefinition = ( - dependencies: VegaVisualizationDependencies -): BaseVisTypeOptions => { - const requestHandler = createVegaRequestHandler(dependencies); - +export const createVegaTypeDefinition = (): VisTypeDefinition => { return { name: 'vega', title: 'Vega', @@ -52,7 +45,6 @@ export const createVegaTypeDefinition = ( enableAutoApply: true, defaultSize: DefaultEditorSize.MEDIUM, }, - requestHandler, toExpressionAst, options: { showIndexSelection: false, @@ -73,5 +65,9 @@ export const createVegaTypeDefinition = ( return []; }, inspectorAdapters: createInspectorAdapters, + /** + * This is necessary for showing actions bar in top of vega editor + */ + requiresSearch: true, }; }; diff --git a/src/plugins/vis_type_vislib/public/area.ts b/src/plugins/vis_type_vislib/public/area.ts index 7458d0a44ecae..cbb705f4cce5d 100644 --- a/src/plugins/vis_type_vislib/public/area.ts +++ b/src/plugins/vis_type_vislib/public/area.ts @@ -7,13 +7,12 @@ */ import { xyVisTypes } from '../../vis_type_xy/public'; -import { BaseVisTypeOptions } from '../../visualizations/public'; +import { VisTypeDefinition } from '../../visualizations/public'; import { toExpressionAst } from './to_ast'; import { BasicVislibParams } from './types'; -export const areaVisTypeDefinition: BaseVisTypeOptions = { - ...(xyVisTypes.area() as BaseVisTypeOptions), +export const areaVisTypeDefinition = { + ...xyVisTypes.area(), toExpressionAst, - visualization: undefined, -}; +} as VisTypeDefinition; diff --git a/src/plugins/vis_type_vislib/public/editor/components/gauge/index.tsx b/src/plugins/vis_type_vislib/public/editor/components/gauge/index.tsx index fa0091b2082b7..e9651954ed302 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/gauge/index.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/gauge/index.tsx @@ -9,20 +9,20 @@ import React, { useCallback } from 'react'; import { EuiSpacer } from '@elastic/eui'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { GaugeVisParams } from '../../../gauge'; import { RangesPanel } from './ranges_panel'; import { StylePanel } from './style_panel'; import { LabelsPanel } from './labels_panel'; -export type GaugeOptionsInternalProps = VisOptionsProps & { +export type GaugeOptionsInternalProps = VisEditorOptionsProps & { setGaugeValue: ( paramName: T, value: GaugeVisParams['gauge'][T] ) => void; }; -function GaugeOptions(props: VisOptionsProps) { +function GaugeOptions(props: VisEditorOptionsProps) { const { stateParams, setValue } = props; const setGaugeValue: GaugeOptionsInternalProps['setGaugeValue'] = useCallback( diff --git a/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx b/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx index 7092615d9fcaf..2dd4b06a52288 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx @@ -12,9 +12,9 @@ import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { ValueAxis } from '../../../../../vis_type_xy/public'; import { - VisOptionsProps, BasicOptions, SelectOption, SwitchOption, @@ -28,7 +28,7 @@ import { import { HeatmapVisParams } from '../../../heatmap'; import { LabelsPanel } from './labels_panel'; -function HeatmapOptions(props: VisOptionsProps) { +function HeatmapOptions(props: VisEditorOptionsProps) { const { stateParams, vis, uiState, setValue, setValidity, setTouched } = props; const [valueAxis] = stateParams.valueAxes; const isColorsNumberInvalid = stateParams.colorsNumber < 2 || stateParams.colorsNumber > 10; diff --git a/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx b/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx index cc224f8e1c6aa..ff98a494ad906 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx @@ -12,7 +12,8 @@ import { EuiColorPicker, EuiFormRow, EuiPanel, EuiSpacer, EuiTitle } from '@elas import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps, SwitchOption } from '../../../../../vis_default_editor/public'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; +import { SwitchOption } from '../../../../../vis_default_editor/public'; import { ValueAxis } from '../../../../../vis_type_xy/public'; import { HeatmapVisParams } from '../../../heatmap'; @@ -21,7 +22,7 @@ const VERTICAL_ROTATION = 270; interface LabelsPanelProps { valueAxis: ValueAxis; - setValue: VisOptionsProps['setValue']; + setValue: VisEditorOptionsProps['setValue']; } function LabelsPanel({ valueAxis, setValue }: LabelsPanelProps) { diff --git a/src/plugins/vis_type_vislib/public/editor/components/index.tsx b/src/plugins/vis_type_vislib/public/editor/components/index.tsx index 264c3066f13fe..6c6181246d6db 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/index.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/index.tsx @@ -8,8 +8,7 @@ import React, { lazy } from 'react'; -import { VisOptionsProps } from '../../../../vis_default_editor/public'; - +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; import { GaugeVisParams } from '../../gauge'; import { PieVisParams } from '../../pie'; import { HeatmapVisParams } from '../../heatmap'; @@ -18,12 +17,14 @@ const GaugeOptionsLazy = lazy(() => import('./gauge')); const PieOptionsLazy = lazy(() => import('./pie')); const HeatmapOptionsLazy = lazy(() => import('./heatmap')); -export const GaugeOptions = (props: VisOptionsProps) => ( +export const GaugeOptions = (props: VisEditorOptionsProps) => ( ); -export const PieOptions = (props: VisOptionsProps) => ; +export const PieOptions = (props: VisEditorOptionsProps) => ( + +); -export const HeatmapOptions = (props: VisOptionsProps) => ( +export const HeatmapOptions = (props: VisEditorOptionsProps) => ( ); diff --git a/src/plugins/vis_type_vislib/public/editor/components/pie.tsx b/src/plugins/vis_type_vislib/public/editor/components/pie.tsx index 863b11c6a3fcf..e0ea1fcee4881 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/pie.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/pie.tsx @@ -12,12 +12,13 @@ import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { BasicOptions, SwitchOption, VisOptionsProps } from '../../../../vis_default_editor/public'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; +import { BasicOptions, SwitchOption } from '../../../../vis_default_editor/public'; import { TruncateLabelsOption } from '../../../../vis_type_xy/public'; import { PieVisParams } from '../../pie'; -function PieOptions(props: VisOptionsProps) { +function PieOptions(props: VisEditorOptionsProps) { const { stateParams, setValue } = props; const setLabels = ( paramName: T, diff --git a/src/plugins/vis_type_vislib/public/gauge.ts b/src/plugins/vis_type_vislib/public/gauge.ts index 5dc59f62edffd..e0c63fd15e3d7 100644 --- a/src/plugins/vis_type_vislib/public/gauge.ts +++ b/src/plugins/vis_type_vislib/public/gauge.ts @@ -11,9 +11,9 @@ import { i18n } from '@kbn/i18n'; import { ColorMode, ColorSchemas, ColorSchemaParams, Labels, Style } from '../../charts/public'; import { RangeValues } from '../../vis_default_editor/public'; import { AggGroupNames } from '../../data/public'; -import { BaseVisTypeOptions, VIS_EVENT_TO_TRIGGER } from '../../visualizations/public'; +import { VisTypeDefinition, VIS_EVENT_TO_TRIGGER } from '../../visualizations/public'; -import { Alignment, GaugeType, BasicVislibParams, VislibChartType } from './types'; +import { Alignment, GaugeType, VislibChartType } from './types'; import { getGaugeCollections } from './editor'; import { toExpressionAst } from './to_ast'; import { GaugeOptions } from './editor/components'; @@ -46,7 +46,7 @@ export interface GaugeVisParams { gauge: Gauge; } -export const gaugeVisTypeDefinition: BaseVisTypeOptions = { +export const gaugeVisTypeDefinition: VisTypeDefinition = { name: 'gauge', title: i18n.translate('visTypeVislib.gauge.gaugeTitle', { defaultMessage: 'Gauge' }), icon: 'visGauge', @@ -135,5 +135,5 @@ export const gaugeVisTypeDefinition: BaseVisTypeOptions = { }, ], }, - useCustomNoDataScreen: true, + requiresSearch: true, }; diff --git a/src/plugins/vis_type_vislib/public/goal.ts b/src/plugins/vis_type_vislib/public/goal.ts index 55a9f83daa4ab..6565ae53f3104 100644 --- a/src/plugins/vis_type_vislib/public/goal.ts +++ b/src/plugins/vis_type_vislib/public/goal.ts @@ -10,13 +10,14 @@ import { i18n } from '@kbn/i18n'; import { AggGroupNames } from '../../data/public'; import { ColorMode, ColorSchemas } from '../../charts/public'; -import { BaseVisTypeOptions } from '../../visualizations/public'; +import { VisTypeDefinition } from '../../visualizations/public'; import { getGaugeCollections, GaugeOptions } from './editor'; import { toExpressionAst } from './to_ast'; -import { GaugeType, BasicVislibParams } from './types'; +import { GaugeType } from './types'; +import { GaugeVisParams } from './gauge'; -export const goalVisTypeDefinition: BaseVisTypeOptions = { +export const goalVisTypeDefinition: VisTypeDefinition = { name: 'goal', title: i18n.translate('visTypeVislib.goal.goalTitle', { defaultMessage: 'Goal' }), icon: 'visGoal', @@ -98,5 +99,5 @@ export const goalVisTypeDefinition: BaseVisTypeOptions = { }, ], }, - useCustomNoDataScreen: true, + requiresSearch: true, }; diff --git a/src/plugins/vis_type_vislib/public/heatmap.ts b/src/plugins/vis_type_vislib/public/heatmap.ts index 8f891263e354a..1b1f4c6530fef 100644 --- a/src/plugins/vis_type_vislib/public/heatmap.ts +++ b/src/plugins/vis_type_vislib/public/heatmap.ts @@ -12,12 +12,12 @@ import { Position } from '@elastic/charts'; import { RangeValues } from '../../vis_default_editor/public'; import { AggGroupNames } from '../../data/public'; import { ColorSchemas, ColorSchemaParams } from '../../charts/public'; -import { VIS_EVENT_TO_TRIGGER, BaseVisTypeOptions } from '../../visualizations/public'; +import { VIS_EVENT_TO_TRIGGER, VisTypeDefinition } from '../../visualizations/public'; import { ValueAxis, ScaleType, AxisType } from '../../vis_type_xy/public'; import { HeatmapOptions, getHeatmapCollections } from './editor'; import { TimeMarker } from './vislib/visualizations/time_marker'; -import { CommonVislibParams, BasicVislibParams, VislibChartType } from './types'; +import { CommonVislibParams, VislibChartType } from './types'; import { toExpressionAst } from './to_ast'; export interface HeatmapVisParams extends CommonVislibParams, ColorSchemaParams { @@ -32,7 +32,7 @@ export interface HeatmapVisParams extends CommonVislibParams, ColorSchemaParams times: TimeMarker[]; } -export const heatmapVisTypeDefinition: BaseVisTypeOptions = { +export const heatmapVisTypeDefinition: VisTypeDefinition = { name: 'heatmap', title: i18n.translate('visTypeVislib.heatmap.heatmapTitle', { defaultMessage: 'Heat map' }), icon: 'heatmap', @@ -127,4 +127,5 @@ export const heatmapVisTypeDefinition: BaseVisTypeOptions = { }, ], }, + requiresSearch: true, }; diff --git a/src/plugins/vis_type_vislib/public/histogram.ts b/src/plugins/vis_type_vislib/public/histogram.ts index 8116b40e9522e..5162df2e6d985 100644 --- a/src/plugins/vis_type_vislib/public/histogram.ts +++ b/src/plugins/vis_type_vislib/public/histogram.ts @@ -7,13 +7,12 @@ */ import { xyVisTypes } from '../../vis_type_xy/public'; -import { BaseVisTypeOptions } from '../../visualizations/public'; +import { VisTypeDefinition } from '../../visualizations/public'; import { toExpressionAst } from './to_ast'; import { BasicVislibParams } from './types'; -export const histogramVisTypeDefinition: BaseVisTypeOptions = { - ...(xyVisTypes.histogram() as BaseVisTypeOptions), +export const histogramVisTypeDefinition = { + ...xyVisTypes.histogram(), toExpressionAst, - visualization: undefined, -}; +} as VisTypeDefinition; diff --git a/src/plugins/vis_type_vislib/public/horizontal_bar.ts b/src/plugins/vis_type_vislib/public/horizontal_bar.ts index b07261bc89b56..14c214a13e903 100644 --- a/src/plugins/vis_type_vislib/public/horizontal_bar.ts +++ b/src/plugins/vis_type_vislib/public/horizontal_bar.ts @@ -7,13 +7,12 @@ */ import { xyVisTypes } from '../../vis_type_xy/public'; -import { BaseVisTypeOptions } from '../../visualizations/public'; +import { VisTypeDefinition } from '../../visualizations/public'; import { toExpressionAst } from './to_ast'; import { BasicVislibParams } from './types'; -export const horizontalBarVisTypeDefinition: BaseVisTypeOptions = { - ...(xyVisTypes.horizontalBar() as BaseVisTypeOptions), +export const horizontalBarVisTypeDefinition = { + ...xyVisTypes.horizontalBar(), toExpressionAst, - visualization: undefined, -}; +} as VisTypeDefinition; diff --git a/src/plugins/vis_type_vislib/public/line.ts b/src/plugins/vis_type_vislib/public/line.ts index 80840427a016c..33f2cc2969345 100644 --- a/src/plugins/vis_type_vislib/public/line.ts +++ b/src/plugins/vis_type_vislib/public/line.ts @@ -7,13 +7,12 @@ */ import { xyVisTypes } from '../../vis_type_xy/public'; -import { BaseVisTypeOptions } from '../../visualizations/public'; +import { VisTypeDefinition } from '../../visualizations/public'; import { toExpressionAst } from './to_ast'; import { BasicVislibParams } from './types'; -export const lineVisTypeDefinition: BaseVisTypeOptions = { - ...(xyVisTypes.line() as BaseVisTypeOptions), +export const lineVisTypeDefinition = { + ...xyVisTypes.line(), toExpressionAst, - visualization: undefined, -}; +} as VisTypeDefinition; diff --git a/src/plugins/vis_type_vislib/public/pie.ts b/src/plugins/vis_type_vislib/public/pie.ts index 1311706aa2799..974be507ba113 100644 --- a/src/plugins/vis_type_vislib/public/pie.ts +++ b/src/plugins/vis_type_vislib/public/pie.ts @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { Position } from '@elastic/charts'; import { AggGroupNames } from '../../data/public'; -import { BaseVisTypeOptions, VIS_EVENT_TO_TRIGGER } from '../../visualizations/public'; +import { VisTypeDefinition, VIS_EVENT_TO_TRIGGER } from '../../visualizations/public'; import { getPositions } from '../../vis_type_xy/public'; import { CommonVislibParams } from './types'; @@ -28,7 +28,7 @@ export interface PieVisParams extends CommonVislibParams { }; } -export const pieVisTypeDefinition: BaseVisTypeOptions = { +export const pieVisTypeDefinition: VisTypeDefinition = { name: 'pie', title: i18n.translate('visTypeVislib.pie.pieTitle', { defaultMessage: 'Pie' }), icon: 'visPie', @@ -93,5 +93,5 @@ export const pieVisTypeDefinition: BaseVisTypeOptions = { ], }, hierarchicalData: true, - responseHandler: 'vislib_slices', + requiresSearch: true, }; diff --git a/src/plugins/vis_type_vislib/public/to_ast.ts b/src/plugins/vis_type_vislib/public/to_ast.ts index be51ee70e6368..bad3d731a0887 100644 --- a/src/plugins/vis_type_vislib/public/to_ast.ts +++ b/src/plugins/vis_type_vislib/public/to_ast.ts @@ -8,7 +8,12 @@ import moment from 'moment'; -import { VisToExpressionAst, getVisSchemas } from '../../visualizations/public'; +import { + Vis, + VisToExpressionAstParams, + getVisSchemas, + VisParams, +} from '../../visualizations/public'; import { buildExpression, buildExpressionFunction } from '../../expressions/public'; import type { Dimensions, DateHistogramParams, HistogramParams } from '../../vis_type_xy/public'; import { BUCKET_TYPES } from '../../data/public'; @@ -17,7 +22,10 @@ import { vislibVisName, VisTypeVislibExpressionFunctionDefinition } from './vis_ import { BasicVislibParams, VislibChartType } from './types'; import { getEsaggsFn } from './to_ast_esaggs'; -export const toExpressionAst: VisToExpressionAst = async (vis, params) => { +export const toExpressionAst = async ( + vis: Vis, + params: VisToExpressionAstParams +) => { const schemas = getVisSchemas(vis, params); const dimensions: Dimensions = { x: schemas.segment ? schemas.segment[0] : null, @@ -58,9 +66,11 @@ export const toExpressionAst: VisToExpressionAst = async (vis (dimensions.y || []).forEach((yDimension) => { const yAgg = responseAggs.filter(({ enabled }) => enabled)[yDimension.accessor]; - const seriesParam = (visConfig.seriesParams || []).find((param) => param.data.id === yAgg.id); + const seriesParam = ((visConfig.seriesParams as BasicVislibParams['seriesParams']) || []).find( + (param) => param.data.id === yAgg.id + ); if (seriesParam) { - const usedValueAxis = (visConfig.valueAxes || []).find( + const usedValueAxis = ((visConfig.valueAxes as BasicVislibParams['valueAxes']) || []).find( (valueAxis) => valueAxis.id === seriesParam.valueAxis ); if (usedValueAxis?.scale.mode === 'percentage') { @@ -72,13 +82,11 @@ export const toExpressionAst: VisToExpressionAst = async (vis } }); - visConfig.dimensions = dimensions; - const visTypeVislib = buildExpressionFunction( vislibVisName, { type: vis.type.name as Exclude, - visConfig: JSON.stringify(visConfig), + visConfig: JSON.stringify({ ...visConfig, dimensions }), } ); diff --git a/src/plugins/vis_type_vislib/public/to_ast_esaggs.ts b/src/plugins/vis_type_vislib/public/to_ast_esaggs.ts index 2e73eea393d65..9de486fc1ba04 100644 --- a/src/plugins/vis_type_vislib/public/to_ast_esaggs.ts +++ b/src/plugins/vis_type_vislib/public/to_ast_esaggs.ts @@ -13,16 +13,13 @@ import { IndexPatternLoadExpressionFunctionDefinition, } from '../../data/public'; -import { PieVisParams } from './pie'; -import { BasicVislibParams } from './types'; - /** * Get esaggs expressions function * TODO: replace this with vis.data.aggs!.toExpressionAst(); * https://github.com/elastic/kibana/issues/61768 * @param vis */ -export function getEsaggsFn(vis: Vis | Vis) { +export function getEsaggsFn(vis: Vis) { return buildExpressionFunction('esaggs', { index: buildExpression([ buildExpressionFunction('indexPatternLoad', { diff --git a/src/plugins/vis_type_vislib/public/vis_type_vislib_vis_types.ts b/src/plugins/vis_type_vislib/public/vis_type_vislib_vis_types.ts index c80ddbd8976b3..52212741fe9df 100644 --- a/src/plugins/vis_type_vislib/public/vis_type_vislib_vis_types.ts +++ b/src/plugins/vis_type_vislib/public/vis_type_vislib_vis_types.ts @@ -6,6 +6,7 @@ * Public License, v 1. */ +import { VisTypeDefinition } from 'src/plugins/visualizations/public'; import { histogramVisTypeDefinition } from './histogram'; import { lineVisTypeDefinition } from './line'; import { areaVisTypeDefinition } from './area'; @@ -16,7 +17,7 @@ import { goalVisTypeDefinition } from './goal'; export { pieVisTypeDefinition } from './pie'; -export const visLibVisTypeDefinitions = [ +export const visLibVisTypeDefinitions: Array> = [ histogramVisTypeDefinition, lineVisTypeDefinition, areaVisTypeDefinition, @@ -26,7 +27,7 @@ export const visLibVisTypeDefinitions = [ goalVisTypeDefinition, ]; -export const convertedTypeDefinitions = [ +export const convertedTypeDefinitions: Array> = [ heatmapVisTypeDefinition, gaugeVisTypeDefinition, goalVisTypeDefinition, diff --git a/src/plugins/vis_type_xy/public/editor/collections.ts b/src/plugins/vis_type_xy/public/editor/collections.ts index e976ceebb1353..bfce92abdb7ef 100644 --- a/src/plugins/vis_type_xy/public/editor/collections.ts +++ b/src/plugins/vis_type_xy/public/editor/collections.ts @@ -143,39 +143,38 @@ export const getRotateOptions = () => [ }, ]; -export const getFittingFunctions = () => - [ - { - value: Fit.None, - text: i18n.translate('visTypeXy.fittingFunctionsTitle.none', { - defaultMessage: 'Hide (Do not fill gaps)', - }), - }, - { - value: Fit.Zero, - text: i18n.translate('visTypeXy.fittingFunctionsTitle.zero', { - defaultMessage: 'Zero (Fill gaps with zeros)', - }), - }, - { - value: Fit.Linear, - text: i18n.translate('visTypeXy.fittingFunctionsTitle.linear', { - defaultMessage: 'Linear (Fill gaps with a line)', - }), - }, - { - value: Fit.Carry, - text: i18n.translate('visTypeXy.fittingFunctionsTitle.carry', { - defaultMessage: 'Last (Fill gaps with the last value)', - }), - }, - { - value: Fit.Lookahead, - text: i18n.translate('visTypeXy.fittingFunctionsTitle.lookahead', { - defaultMessage: 'Next (Fill gaps with the next value)', - }), - }, - ] as const; +export const getFittingFunctions = () => [ + { + value: Fit.None, + text: i18n.translate('visTypeXy.fittingFunctionsTitle.none', { + defaultMessage: 'Hide (Do not fill gaps)', + }), + }, + { + value: Fit.Zero, + text: i18n.translate('visTypeXy.fittingFunctionsTitle.zero', { + defaultMessage: 'Zero (Fill gaps with zeros)', + }), + }, + { + value: Fit.Linear, + text: i18n.translate('visTypeXy.fittingFunctionsTitle.linear', { + defaultMessage: 'Linear (Fill gaps with a line)', + }), + }, + { + value: Fit.Carry, + text: i18n.translate('visTypeXy.fittingFunctionsTitle.carry', { + defaultMessage: 'Last (Fill gaps with the last value)', + }), + }, + { + value: Fit.Lookahead, + text: i18n.translate('visTypeXy.fittingFunctionsTitle.lookahead', { + defaultMessage: 'Next (Fill gaps with the next value)', + }), + }, +]; export const getConfigCollections = () => ({ legendPositions: getPositions(), diff --git a/src/plugins/vis_type_xy/public/editor/common_config.tsx b/src/plugins/vis_type_xy/public/editor/common_config.tsx index 4cfb18b4a0b11..1d635463cd963 100644 --- a/src/plugins/vis_type_xy/public/editor/common_config.tsx +++ b/src/plugins/vis_type_xy/public/editor/common_config.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from '../../../vis_default_editor/public'; +import { VisEditorOptionsProps } from '../../../visualizations/public'; import { VisParams } from '../types'; import { MetricsAxisOptions, PointSeriesOptions } from './components/options'; @@ -22,7 +22,7 @@ export function getOptionTabs(showElasticChartsOptions = false) { title: i18n.translate('visTypeXy.area.tabs.metricsAxesTitle', { defaultMessage: 'Metrics & axes', }), - editor: (props: VisOptionsProps) => ( + editor: (props: VisEditorOptionsProps) => ( ), }, @@ -31,7 +31,7 @@ export function getOptionTabs(showElasticChartsOptions = false) { title: i18n.translate('visTypeXy.area.tabs.panelSettingsTitle', { defaultMessage: 'Panel settings', }), - editor: (props: VisOptionsProps) => ( + editor: (props: VisEditorOptionsProps) => ( extends VisOptionsProps { +export interface ValidationVisOptionsProps extends VisEditorOptionsProps { setMultipleValidity(paramName: string, isValid: boolean): void; extraProps?: E; } -interface ValidationWrapperProps extends VisOptionsProps { +interface ValidationWrapperProps extends VisEditorOptionsProps { component: React.ComponentType>; extraProps?: E; } diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx index edb6cfac3c2dd..f863d668985d8 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx @@ -13,11 +13,8 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { Position } from '@elastic/charts'; -import { - SelectOption, - SwitchOption, - VisOptionsProps, -} from '../../../../../../vis_default_editor/public'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; +import { SelectOption, SwitchOption } from '../../../../../../vis_default_editor/public'; import { LabelOptions, SetAxisLabel } from './label_options'; import { CategoryAxis } from '../../../../types'; @@ -26,7 +23,7 @@ export interface CategoryAxisPanelProps { axis: CategoryAxis; onPositionChanged: (position: Position) => void; setCategoryAxis: (value: CategoryAxis) => void; - vis: VisOptionsProps['vis']; + vis: VisEditorOptionsProps['vis']; } function CategoryAxisPanel({ diff --git a/src/plugins/vis_type_xy/public/sample_vis.test.mocks.ts b/src/plugins/vis_type_xy/public/sample_vis.test.mocks.ts index 324ea96c082ba..880161293ca25 100644 --- a/src/plugins/vis_type_xy/public/sample_vis.test.mocks.ts +++ b/src/plugins/vis_type_xy/public/sample_vis.test.mocks.ts @@ -106,10 +106,7 @@ export const samplePieVis = { }, }, hidden: false, - requestHandler: 'courier', - responseHandler: 'vislib_slices', hierarchicalData: true, - useCustomNoDataScreen: false, }, title: '[Flights] Airline Carrier', description: '', @@ -126,7 +123,6 @@ export const samplePieVis = { truncate: 100, }, }, - sessionState: {}, data: { searchSource: { id: 'data_source1', @@ -1622,10 +1618,7 @@ export const sampleAreaVis = { }, }, hidden: false, - requestHandler: 'courier', - responseHandler: 'none', hierarchicalData: false, - useCustomNoDataScreen: false, }, title: '[eCommerce] Sales by Category', description: '', @@ -1762,7 +1755,6 @@ export const sampleAreaVis = { ], }, }, - sessionState: {}, data: { searchSource: { id: 'data_source1', diff --git a/src/plugins/vis_type_xy/public/types/vis_type.ts b/src/plugins/vis_type_xy/public/types/vis_type.ts index 3decb8854d355..19b8b88ec5f66 100644 --- a/src/plugins/vis_type_xy/public/types/vis_type.ts +++ b/src/plugins/vis_type_xy/public/types/vis_type.ts @@ -6,16 +6,11 @@ * Public License, v 1. */ -import { BaseVisTypeOptions } from '../../../visualizations/public'; +import { VisTypeDefinition } from '../../../visualizations/public'; import { ChartType } from '../../common'; import { VisParams } from './param'; export type VisTypeNames = ChartType | 'horizontal_bar'; -export type XyVisTypeDefinition = BaseVisTypeOptions & { - name: VisTypeNames; - visConfig: { - defaults: Omit; - }; -}; +export type XyVisTypeDefinition = VisTypeDefinition; diff --git a/src/plugins/vis_type_xy/public/vis_types/area.ts b/src/plugins/vis_type_xy/public/vis_types/area.ts index 09007a01ca8bc..24045a5c308b4 100644 --- a/src/plugins/vis_type_xy/public/vis_types/area.ts +++ b/src/plugins/vis_type_xy/public/vis_types/area.ts @@ -181,4 +181,5 @@ export const getAreaVisTypeDefinition = ( }, ], }, + requiresSearch: true, }); diff --git a/src/plugins/vis_type_xy/public/vis_types/histogram.ts b/src/plugins/vis_type_xy/public/vis_types/histogram.ts index daae5f5e48e61..c5a2825e7420b 100644 --- a/src/plugins/vis_type_xy/public/vis_types/histogram.ts +++ b/src/plugins/vis_type_xy/public/vis_types/histogram.ts @@ -184,4 +184,5 @@ export const getHistogramVisTypeDefinition = ( }, ], }, + requiresSearch: true, }); diff --git a/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts b/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts index 9e026fa0d7474..b6821b268f5c5 100644 --- a/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts +++ b/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts @@ -183,4 +183,5 @@ export const getHorizontalBarVisTypeDefinition = ( }, ], }, + requiresSearch: true, }); diff --git a/src/plugins/vis_type_xy/public/vis_types/line.ts b/src/plugins/vis_type_xy/public/vis_types/line.ts index 3f3087207fa19..093b7efb6af36 100644 --- a/src/plugins/vis_type_xy/public/vis_types/line.ts +++ b/src/plugins/vis_type_xy/public/vis_types/line.ts @@ -175,4 +175,5 @@ export const getLineVisTypeDefinition = ( }, ], }, + requiresSearch: true, }); diff --git a/src/plugins/visualizations/public/components/__snapshots__/visualization_requesterror.test.js.snap b/src/plugins/visualizations/public/components/__snapshots__/visualization_requesterror.test.js.snap deleted file mode 100644 index d1dde7340cfa1..0000000000000 --- a/src/plugins/visualizations/public/components/__snapshots__/visualization_requesterror.test.js.snap +++ /dev/null @@ -1,24 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`VisualizationRequestError should render according to snapshot 1`] = ` -

-
-
- -
- Request error -
-
-
-`; diff --git a/src/plugins/visualizations/public/components/_visualization.scss b/src/plugins/visualizations/public/components/_visualization.scss index e8d4c5c6db445..42b59b8de93cd 100644 --- a/src/plugins/visualizations/public/components/_visualization.scss +++ b/src/plugins/visualizations/public/components/_visualization.scss @@ -15,48 +15,6 @@ position: relative; padding: $euiSizeS; flex: 1 1 100%; - - /** - * 1. Expand to fill the container but accept being squeezed smaller by the spy, even so small - * that it disappears entirely. - */ - .visChart__container { - @include euiScrollBar; - min-height: 0; - flex: 1 1 0; /* 1 */ - display: flex; - flex-direction: row; - overflow: auto; - transition: opacity .01s; - - // IE11 Hack - // - // Normally we would just set flex: 1 1 0%, which works as expected in IE11. - // Unfortunately, a recent bug in Firefox causes this rule to be ignored, so we - // have to use an IE-specific hack instead. - @include internetExplorerOnly() { - flex: 1 0; - } - - &:focus { - box-shadow: none; - } - } - - // SASSTODO: Can't find exact usage - .loading { - opacity: .5; - } - - // SASSTODO: Can't find exact usage - .spinner { - position: absolute; - top: 40%; - left: 0; - right: 0; - z-index: 20; - opacity: .5; - } } .visChart { diff --git a/src/plugins/visualizations/public/components/index.ts b/src/plugins/visualizations/public/components/index.ts index 88f3acf20d39b..1d6b6b5d911ea 100644 --- a/src/plugins/visualizations/public/components/index.ts +++ b/src/plugins/visualizations/public/components/index.ts @@ -6,6 +6,4 @@ * Public License, v 1. */ -export { Visualization } from './visualization'; export { VisualizationContainer } from './visualization_container'; -export { VisualizationNoResults } from './visualization_noresults'; diff --git a/src/plugins/visualizations/public/components/visualization.test.js b/src/plugins/visualizations/public/components/visualization.test.js deleted file mode 100644 index cb332232cbc55..0000000000000 --- a/src/plugins/visualizations/public/components/visualization.test.js +++ /dev/null @@ -1,92 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -jest.useFakeTimers(); - -import React from 'react'; -import { render, mount } from 'enzyme'; -import { Visualization } from './visualization'; - -let renderPromise; -class VisualizationStub { - constructor(el, vis) { - this.el = el; - this.vis = vis; - } - - render() { - renderPromise = new Promise((resolve) => { - this.el.innerText = this.vis.params.markdown; - resolve(); - }); - - return renderPromise; - } -} - -describe('', () => { - const visData = { - hits: 1, - }; - - const uiState = { - on: () => {}, - off: () => {}, - set: () => {}, - }; - - let vis; - - beforeEach(() => { - vis = { - setUiState: function (uiState) { - this.uiState = uiState; - }, - getUiState: function () { - return this.uiState; - }, - params: {}, - type: { - title: 'new vis', - requiresSearch: true, - useCustomNoDataScreen: false, - visualization: VisualizationStub, - }, - }; - }); - - it('should display no result message when length of data is 0', () => { - const data = { rows: [] }; - const wrapper = render( - - ); - expect(wrapper.text()).toBe('No results found'); - }); - - it('should render chart when data is present', () => { - const wrapper = render( - - ); - expect(wrapper.text()).not.toBe('No results found'); - }); - - it('should call onInit when rendering no data', () => { - const spy = jest.fn(); - const noData = { hits: 0 }; - mount( - - ); - expect(spy).toHaveBeenCalled(); - }); -}); diff --git a/src/plugins/visualizations/public/components/visualization.tsx b/src/plugins/visualizations/public/components/visualization.tsx deleted file mode 100644 index bdabe6bc07297..0000000000000 --- a/src/plugins/visualizations/public/components/visualization.tsx +++ /dev/null @@ -1,73 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { get } from 'lodash'; -import React from 'react'; -import { PersistedState } from '../../../../plugins/visualizations/public'; -import { memoizeLast } from '../legacy/memoize'; -import { VisualizationChart } from './visualization_chart'; -import { VisualizationNoResults } from './visualization_noresults'; -import { ExprVis } from '../expressions/vis'; - -function shouldShowNoResultsMessage(vis: ExprVis, visData: any): boolean { - const requiresSearch = get(vis, 'type.requiresSearch'); - const rows: object[] | undefined = get(visData, 'rows'); - const isZeroHits = get(visData, 'hits') === 0 || (rows && !rows.length); - const shouldShowMessage = !get(vis, 'type.useCustomNoDataScreen'); - - return Boolean(requiresSearch && isZeroHits && shouldShowMessage); -} - -interface VisualizationProps { - listenOnChange: boolean; - onInit?: () => void; - uiState: PersistedState; - vis: ExprVis; - visData: any; - visParams: any; -} - -export class Visualization extends React.Component { - private showNoResultsMessage = memoizeLast(shouldShowNoResultsMessage); - - constructor(props: VisualizationProps) { - super(props); - - props.vis.setUiState(props.uiState); - } - - public render() { - const { vis, visData, visParams, onInit, uiState, listenOnChange } = this.props; - - const noResults = this.showNoResultsMessage(vis, visData); - - return ( -
- {noResults ? ( - - ) : ( - - )} -
- ); - } - - public shouldComponentUpdate(nextProps: VisualizationProps): boolean { - if (nextProps.uiState !== this.props.uiState) { - throw new Error('Changing uiState on is not supported!'); - } - return true; - } -} diff --git a/src/plugins/visualizations/public/components/visualization_chart.test.js b/src/plugins/visualizations/public/components/visualization_chart.test.js deleted file mode 100644 index 98cc5bb8d0729..0000000000000 --- a/src/plugins/visualizations/public/components/visualization_chart.test.js +++ /dev/null @@ -1,56 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -jest.useFakeTimers(); - -import React from 'react'; -import { render, mount } from 'enzyme'; -import { VisualizationChart } from './visualization_chart'; - -let renderPromise; - -class VisualizationStub { - constructor(el, vis) { - this.el = el; - this.vis = vis; - } - - render() { - renderPromise = new Promise((resolve) => { - this.el.textContent = this.vis.params.markdown; - resolve(); - }); - - return renderPromise; - } -} - -describe('', () => { - const vis = { - type: { - title: 'Test Visualization', - visualization: VisualizationStub, - }, - params: { - markdown: - 'This is a test of the [markdown](http://daringfireball.net/projects/markdown) vis.', - }, - }; - - it('should render initial html', () => { - const wrapper = render(); - expect(wrapper.text()).toBe(''); - }); - - it('should render visualization', async () => { - const wrapper = mount(); - jest.runAllTimers(); - await renderPromise; - expect(wrapper.find('.visChart').text()).toMatch(/markdown/); - }); -}); diff --git a/src/plugins/visualizations/public/components/visualization_chart.tsx b/src/plugins/visualizations/public/components/visualization_chart.tsx deleted file mode 100644 index c6ad1c53f91b7..0000000000000 --- a/src/plugins/visualizations/public/components/visualization_chart.tsx +++ /dev/null @@ -1,134 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import React from 'react'; -import * as Rx from 'rxjs'; -import { debounceTime, filter, share, switchMap } from 'rxjs/operators'; -import { PersistedState } from '../../../../plugins/visualizations/public'; -import { VisualizationController } from '../types'; -import { ResizeChecker } from '../../../../plugins/kibana_utils/public'; -import { ExprVis } from '../expressions/vis'; - -interface VisualizationChartProps { - onInit?: () => void; - uiState: PersistedState; - vis: ExprVis; - visData: any; - visParams: any; - listenOnChange: boolean; -} - -class VisualizationChart extends React.Component { - private resizeChecker?: ResizeChecker; - private visualization?: VisualizationController; - private chartDiv = React.createRef(); - private containerDiv = React.createRef(); - private renderSubject: Rx.Subject<{ - vis: ExprVis; - visParams: any; - visData: any; - }>; - private renderSubscription: Rx.Subscription; - - constructor(props: VisualizationChartProps) { - super(props); - - this.renderSubject = new Rx.Subject(); - const render$ = this.renderSubject.asObservable().pipe(share()); - - const success$ = render$.pipe( - filter(({ vis, visData }) => vis && (!vis.type.requiresSearch || visData)), - debounceTime(100), - switchMap(async ({ vis, visData, visParams }) => { - if (!this.visualization) { - // This should never happen, since we only should trigger another rendering - // after this component has mounted and thus the visualization implementation - // has been initialized - throw new Error('Visualization implementation was not initialized on first render.'); - } - - return this.visualization.render(visData, visParams); - }) - ); - - this.renderSubscription = success$.subscribe(() => { - if (this.props.onInit) { - this.props.onInit(); - } - }); - } - - public render() { - return ( -
-
-
- ); - } - - public componentDidMount() { - if (!this.chartDiv.current || !this.containerDiv.current) { - throw new Error('chartDiv and currentDiv reference should always be present.'); - } - - const { vis } = this.props; - const Visualization = vis.type.visualization; - - if (!Visualization) { - throw new Error( - 'Tried to use VisualizationChart component with a vis without visualization property.' - ); - } - - this.visualization = new Visualization(this.chartDiv.current, vis); - - // We know that containerDiv.current will never be null, since we will always - // have rendered and the div is always rendered into the tree (i.e. not - // inside any condition). - this.resizeChecker = new ResizeChecker(this.containerDiv.current); - this.resizeChecker.on('resize', () => this.startRenderVisualization()); - - if (this.props.listenOnChange) { - this.props.uiState.on('change', this.onUiStateChanged); - } - - this.startRenderVisualization(); - } - - public componentDidUpdate() { - this.startRenderVisualization(); - } - - public componentWillUnmount() { - if (this.renderSubscription) { - this.renderSubscription.unsubscribe(); - } - if (this.resizeChecker) { - this.resizeChecker.destroy(); - } - if (this.visualization) { - this.visualization.destroy(); - } - } - - private onUiStateChanged = () => { - this.startRenderVisualization(); - }; - - private startRenderVisualization(): void { - if (this.containerDiv.current && this.chartDiv.current) { - this.renderSubject.next({ - vis: this.props.vis, - visData: this.props.visData, - visParams: this.props.visParams, - }); - } - } -} - -export { VisualizationChart }; diff --git a/src/plugins/visualizations/public/components/visualization_requesterror.test.js b/src/plugins/visualizations/public/components/visualization_requesterror.test.js deleted file mode 100644 index 8a183efe3faa6..0000000000000 --- a/src/plugins/visualizations/public/components/visualization_requesterror.test.js +++ /dev/null @@ -1,28 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import React from 'react'; -import { render } from 'enzyme'; -import { VisualizationRequestError } from './visualization_requesterror'; - -describe('VisualizationRequestError', () => { - it('should render according to snapshot', () => { - const wrapper = render(); - expect(wrapper).toMatchSnapshot(); - }); - - it('should set html when error is an object', () => { - const wrapper = render(); - expect(wrapper.text()).toBe('Request error'); - }); - - it('should set html when error is a string', () => { - const wrapper = render(); - expect(wrapper.text()).toBe('Request error'); - }); -}); diff --git a/src/plugins/visualizations/public/components/visualization_requesterror.tsx b/src/plugins/visualizations/public/components/visualization_requesterror.tsx deleted file mode 100644 index 3378875327959..0000000000000 --- a/src/plugins/visualizations/public/components/visualization_requesterror.tsx +++ /dev/null @@ -1,51 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { EuiIcon, EuiSpacer, EuiText } from '@elastic/eui'; -import React from 'react'; -import { SearchError } from '../../../../plugins/data/public'; - -interface VisualizationRequestErrorProps { - onInit?: () => void; - error: SearchError | string; -} - -export class VisualizationRequestError extends React.Component { - private containerDiv = React.createRef(); - - public render() { - const { error } = this.props; - const errorMessage = typeof error === 'string' ? error : error.message; - - return ( -
- - - - - - {errorMessage} - -
- ); - } - - public componentDidMount() { - this.afterRender(); - } - - public componentDidUpdate() { - this.afterRender(); - } - - private afterRender() { - if (this.props.onInit) { - this.props.onInit(); - } - } -} diff --git a/src/plugins/visualizations/public/embeddable/_embeddables.scss b/src/plugins/visualizations/public/embeddable/_embeddables.scss index f0ec8479489d2..2a4d5c14a46a5 100644 --- a/src/plugins/visualizations/public/embeddable/_embeddables.scss +++ b/src/plugins/visualizations/public/embeddable/_embeddables.scss @@ -8,10 +8,6 @@ @include euiScrollBar; /* 2 */ } - .visualization .visChart__container { - overflow: visible; /* 1 */ - } - .visLegend__toggle { border-bottom-right-radius: 0; border-top-left-radius: 0; diff --git a/src/plugins/visualizations/public/embeddable/get_index_pattern.ts b/src/plugins/visualizations/public/embeddable/get_index_pattern.ts deleted file mode 100644 index 65c6ebe75daf5..0000000000000 --- a/src/plugins/visualizations/public/embeddable/get_index_pattern.ts +++ /dev/null @@ -1,25 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { VisSavedObject } from '../types'; -import type { IndexPattern } from '../../../../plugins/data/public'; -import { getIndexPatterns } from '../services'; - -export async function getIndexPattern( - savedVis: VisSavedObject -): Promise { - if (savedVis.visState.type !== 'metrics') { - return savedVis.searchSource!.getField('index'); - } - - const indexPatternsClient = getIndexPatterns(); - - return savedVis.visState.params.index_pattern - ? (await indexPatternsClient.find(`"${savedVis.visState.params.index_pattern}"`))[0] - : await indexPatternsClient.getDefault(); -} diff --git a/src/plugins/visualizations/public/embeddable/to_ast.ts b/src/plugins/visualizations/public/embeddable/to_ast.ts new file mode 100644 index 0000000000000..31a9db5b92d11 --- /dev/null +++ b/src/plugins/visualizations/public/embeddable/to_ast.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { ExpressionFunctionKibana, ExpressionFunctionKibanaContext } from '../../../data/public'; +import { buildExpression, buildExpressionFunction } from '../../../expressions/public'; + +import { VisToExpressionAst } from '../types'; + +/** + * Creates an ast expression for a visualization based on kibana context (query, filters, timerange) + * including a saved search if the visualization is based on it. + * The expression also includes particular visualization expression ast if presented. + * + * @internal + */ +export const toExpressionAst: VisToExpressionAst = async (vis, params) => { + const { savedSearchId, searchSource } = vis.data; + const query = searchSource?.getField('query'); + const filters = searchSource?.getField('filter'); + + const kibana = buildExpressionFunction('kibana', {}); + const kibanaContext = buildExpressionFunction('kibana_context', { + q: query && JSON.stringify(query), + filters: filters && JSON.stringify(filters), + savedSearchId, + }); + + const ast = buildExpression([kibana, kibanaContext]); + const expression = ast.toAst(); + + if (!vis.type.toExpressionAst) { + throw new Error('Visualization type definition should have toExpressionAst function defined'); + } + + const visExpressionAst = await vis.type.toExpressionAst(vis, params); + // expand the expression chain with a particular visualization expression chain, if it exists + expression.chain.push(...visExpressionAst.chain); + + return expression; +}; diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index 88fc6e6a98c0d..45bd6a8a9d554 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -32,8 +32,8 @@ import { IExpressionLoaderParams, ExpressionsStart, ExpressionRenderError, + ExpressionAstExpression, } from '../../../../plugins/expressions/public'; -import { buildPipeline } from '../legacy/build_pipeline'; import { Vis, SerializedVis } from '../vis'; import { getExpressions, getUiActions } from '../services'; import { VIS_EVENT_TO_TRIGGER } from './events'; @@ -41,6 +41,7 @@ import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory'; import { SavedObjectAttributes } from '../../../../core/types'; import { SavedVisualizationsLoader } from '../saved_visualizations'; import { VisSavedObject } from '../types'; +import { toExpressionAst } from './to_ast'; const getKeys = (o: T): Array => Object.keys(o) as Array; @@ -94,7 +95,7 @@ export class VisualizeEmbeddable private syncColors?: boolean; private visCustomizations?: Pick; private subscriptions: Subscription[] = []; - private expression: string = ''; + private expression?: ExpressionAstExpression; private vis: Vis; private domNode: any; public readonly type = VISUALIZE_EMBEDDABLE_TYPE; @@ -382,7 +383,7 @@ export class VisualizeEmbeddable } this.abortController = new AbortController(); const abortController = this.abortController; - this.expression = await buildPipeline(this.vis, { + this.expression = await toExpressionAst(this.vis, { timefilter: this.timefilter, timeRange: this.timeRange, abortSignal: this.abortController!.signal, diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx index 399e0e4b71532..6a57bf7b4477a 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx @@ -74,12 +74,12 @@ export class VisualizeEmbeddableFactory type: 'visualization', getIconForSavedObject: (savedObject) => { return ( - getTypes().get(JSON.parse(savedObject.attributes.visState).type).icon || 'visualizeApp' + getTypes().get(JSON.parse(savedObject.attributes.visState).type)?.icon || 'visualizeApp' ); }, getTooltipForSavedObject: (savedObject) => { return `${savedObject.attributes.title} (${ - getTypes().get(JSON.parse(savedObject.attributes.visState).type).title + getTypes().get(JSON.parse(savedObject.attributes.visState).type)?.title })`; }, showSavedObject: (savedObject) => { diff --git a/src/plugins/visualizations/public/expressions/vis.ts b/src/plugins/visualizations/public/expressions/vis.ts deleted file mode 100644 index a3952d284805d..0000000000000 --- a/src/plugins/visualizations/public/expressions/vis.ts +++ /dev/null @@ -1,157 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -/** - * @name Vis - * - * @description This class consists of aggs, params, listeners, title, and type. - * - Aggs: Instances of AggConfig. - * - Params: The settings in the Options tab. - * - * Not to be confused with vislib/vis.js. - */ - -import { EventEmitter } from 'events'; -import _ from 'lodash'; -import { VisParams, PersistedState } from '../../../../plugins/visualizations/public'; - -import { getTypes } from '../services'; -import { VisType } from '../vis_types'; - -export interface ExprVisState { - title?: string; - type: VisType | string; - params?: VisParams; -} - -export interface ExprVisAPIEvents { - filter: (data: any) => void; - brush: (data: any) => void; - applyFilter: (data: any) => void; -} - -export interface ExprVisAPI { - events: ExprVisAPIEvents; -} - -export class ExprVis extends EventEmitter { - public title: string = ''; - public type: VisType; - public params: VisParams = {}; - public sessionState: Record = {}; - public API: ExprVisAPI; - public eventsSubject: any; - private uiState: PersistedState; - - constructor(visState: ExprVisState = { type: 'histogram' }) { - super(); - - this.type = this.getType(visState.type); - this.uiState = new PersistedState(); - this.setState(visState); - - this.API = { - events: { - filter: (data: any) => { - if (!this.eventsSubject) return; - this.eventsSubject.next({ - name: 'filterBucket', - data: data.data - ? { - data: data.data, - negate: data.negate, - } - : { data: [data] }, - }); - }, - brush: (data: any) => { - if (!this.eventsSubject) return; - this.eventsSubject.next({ name: 'brush', data }); - }, - applyFilter: (data: any) => { - if (!this.eventsSubject) return; - this.eventsSubject.next({ name: 'applyFilter', data }); - }, - }, - }; - } - - private getType(type: string | VisType) { - if (_.isString(type)) { - const newType = getTypes().get(type); - if (!newType) { - throw new Error(`Invalid type "${type}"`); - } - return newType; - } else { - return type; - } - } - - setState(state: ExprVisState) { - this.title = state.title || ''; - if (state.type) { - this.type = this.getType(state.type); - } - this.params = _.defaultsDeep( - {}, - _.cloneDeep(state.params || {}), - _.cloneDeep(this.type.visConfig.defaults || {}) - ); - } - - getState() { - return { - title: this.title, - type: this.type.name, - params: _.cloneDeep(this.params), - }; - } - - updateState() { - this.emit('update'); - } - - forceReload() { - this.emit('reload'); - } - - isHierarchical() { - if (_.isFunction(this.type.hierarchicalData)) { - return !!this.type.hierarchicalData(this); - } else { - return !!this.type.hierarchicalData; - } - } - - hasUiState() { - return !!this.uiState; - } - - getUiState() { - return this.uiState; - } - - setUiState(state: PersistedState) { - this.uiState = state; - } - - /** - * Currently this is only used to extract map-specific information - * (e.g. mapZoom, mapCenter). - */ - uiStateVal(key: string, val: any) { - if (this.hasUiState()) { - if (_.isUndefined(val)) { - return this.uiState.get(key); - } - return this.uiState.set(key, val); - } - return val; - } -} diff --git a/src/plugins/visualizations/public/expressions/visualization_function.ts b/src/plugins/visualizations/public/expressions/visualization_function.ts deleted file mode 100644 index 623fb303baccc..0000000000000 --- a/src/plugins/visualizations/public/expressions/visualization_function.ts +++ /dev/null @@ -1,144 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { get } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { VisResponseValue, PersistedState } from '../../../../plugins/visualizations/public'; -import { ExpressionFunctionDefinition, Render } from '../../../../plugins/expressions/public'; -import { getTypes, getIndexPatterns, getFilterManager, getSearch } from '../services'; - -interface Arguments { - index?: string | null; - metricsAtAllLevels?: boolean; - partialRows?: boolean; - type?: string; - schemas?: string; - visConfig?: string; - uiState?: string; - aggConfigs?: string; -} - -export type ExpressionFunctionVisualization = ExpressionFunctionDefinition< - 'visualization', - any, - Arguments, - Promise> ->; - -export const visualization = (): ExpressionFunctionVisualization => ({ - name: 'visualization', - type: 'render', - help: i18n.translate('visualizations.functions.visualization.help', { - defaultMessage: 'A simple visualization', - }), - args: { - // TODO: Below `help` keys should be internationalized once this function - // TODO: is moved to visualizations plugin. - index: { - types: ['string', 'null'], - default: null, - help: 'Index', - }, - metricsAtAllLevels: { - types: ['boolean'], - default: false, - help: 'Metrics levels', - }, - partialRows: { - types: ['boolean'], - default: false, - help: 'Partial rows', - }, - type: { - types: ['string'], - default: '', - help: 'Type', - }, - schemas: { - types: ['string'], - default: '"{}"', - help: 'Schemas', - }, - visConfig: { - types: ['string'], - default: '"{}"', - help: 'Visualization configuration', - }, - uiState: { - types: ['string'], - default: '"{}"', - help: 'User interface state', - }, - aggConfigs: { - types: ['string'], - default: '"{}"', - help: 'Aggregation configurations', - }, - }, - async fn(input, args, { inspectorAdapters }) { - const visConfigParams = args.visConfig ? JSON.parse(args.visConfig) : {}; - const schemas = args.schemas ? JSON.parse(args.schemas) : {}; - const visType = getTypes().get(args.type || 'histogram') as any; - const indexPattern = args.index ? await getIndexPatterns().get(args.index) : null; - - const uiStateParams = args.uiState ? JSON.parse(args.uiState) : {}; - const uiState = new PersistedState(uiStateParams); - - const aggConfigsState = args.aggConfigs ? JSON.parse(args.aggConfigs) : []; - const aggs = indexPattern - ? getSearch().aggs.createAggConfigs(indexPattern, aggConfigsState) - : undefined; - - if (typeof visType.requestHandler === 'function') { - input = await visType.requestHandler({ - partialRows: args.partialRows, - metricsAtAllLevels: args.metricsAtAllLevels, - index: indexPattern, - visParams: visConfigParams, - timeRange: get(input, 'timeRange', null), - query: get(input, 'query', null), - filters: get(input, 'filters', null), - uiState, - inspectorAdapters, - queryFilter: getFilterManager(), - aggs, - }); - } - - if (typeof visType.responseHandler === 'function') { - if (input.columns) { - // assign schemas to aggConfigs - input.columns.forEach((column: any) => { - if (column.aggConfig) { - column.aggConfig.aggConfigs.schemas = visType.schemas.all; - } - }); - - Object.keys(schemas).forEach((key) => { - schemas[key].forEach((i: any) => { - if (input.columns[i] && input.columns[i].aggConfig) { - input.columns[i].aggConfig.schema = key; - } - }); - }); - } - - input = await visType.responseHandler(input, visConfigParams.dimensions); - } - - return { - type: 'render', - as: 'visualization', - value: { - visData: input, - visType: args.type || '', - visConfig: visConfigParams, - }, - }; - }, -}); diff --git a/src/plugins/visualizations/public/expressions/visualization_renderer.tsx b/src/plugins/visualizations/public/expressions/visualization_renderer.tsx deleted file mode 100644 index 3547a5d7e20ce..0000000000000 --- a/src/plugins/visualizations/public/expressions/visualization_renderer.tsx +++ /dev/null @@ -1,51 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -// @ts-ignore -import { ExprVis } from './vis'; -import { Visualization } from '../components'; -import { VisParams } from '../types'; - -export const visualization = () => ({ - name: 'visualization', - displayName: 'visualization', - reuseDomNode: true, - render: async (domNode: HTMLElement, config: any, handlers: any) => { - const { visData, visConfig, params } = config; - const visType = config.visType || visConfig.type; - - const vis = new ExprVis({ - title: config.title, - type: visType as string, - params: visConfig as VisParams, - }); - - vis.eventsSubject = { next: handlers.event }; - - const uiState = handlers.uiState || vis.getUiState(); - - handlers.onDestroy(() => { - unmountComponentAtNode(domNode); - }); - - const listenOnChange = params ? params.listenOnChange : false; - render( - , - domNode - ); - }, -}); diff --git a/src/plugins/visualizations/public/index.ts b/src/plugins/visualizations/public/index.ts index 0bf8aa6e5c418..b4216e1fc16af 100644 --- a/src/plugins/visualizations/public/index.ts +++ b/src/plugins/visualizations/public/index.ts @@ -10,7 +10,6 @@ import { PublicContract } from '@kbn/utility-types'; import { PluginInitializerContext } from 'src/core/public'; import { VisualizationsPlugin, VisualizationsSetup, VisualizationsStart } from './plugin'; import { VisualizeEmbeddableFactory, VisualizeEmbeddable } from './embeddable'; -import { ExprVis as ExprVisClass } from './expressions/vis'; export function plugin(initializerContext: PluginInitializerContext) { return new VisualizationsPlugin(initializerContext); @@ -20,39 +19,27 @@ export function plugin(initializerContext: PluginInitializerContext) { export { Vis } from './vis'; export { TypesService } from './vis_types/types_service'; export { VISUALIZE_EMBEDDABLE_TYPE, VIS_EVENT_TO_TRIGGER } from './embeddable'; -export { VisualizationContainer, VisualizationNoResults } from './components'; -export { getSchemas as getVisSchemas } from './legacy/build_pipeline'; +export { VisualizationContainer } from './components'; +export { getVisSchemas } from './vis_schemas'; /** @public types */ export { VisualizationsSetup, VisualizationsStart }; export { VisGroups } from './vis_types'; -export type { - VisTypeAlias, - VisType, - BaseVisTypeOptions, - ReactVisTypeOptions, - Schema, - ISchemas, -} from './vis_types'; +export type { VisTypeAlias, VisTypeDefinition, Schema, ISchemas } from './vis_types'; export { SerializedVis, SerializedVisData, VisData } from './vis'; export type VisualizeEmbeddableFactoryContract = PublicContract; export type VisualizeEmbeddableContract = PublicContract; export { VisualizeInput } from './embeddable'; -export type ExprVis = ExprVisClass; -export { SchemaConfig, BuildPipelineParams } from './legacy/build_pipeline'; -// @ts-ignore +export { SchemaConfig } from './vis_schemas'; export { updateOldState } from './legacy/vis_update_state'; export { PersistedState } from './persisted_state'; export { - VisualizationControllerConstructor, - VisualizationController, ISavedVis, VisSavedObject, - VisResponseValue, VisToExpressionAst, - VisParams, + VisToExpressionAstParams, + VisEditorOptionsProps, } from './types'; -export { ExprVisAPIEvents } from './expressions/vis'; export { VisualizationListItem, VisualizationStage } from './vis_types/vis_type_alias_registry'; export { VISUALIZE_ENABLE_LABS_SETTING } from '../common/constants'; -export { SavedVisState } from '../common'; +export { SavedVisState, VisParams } from '../common'; diff --git a/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap b/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap deleted file mode 100644 index 3d685064111dc..0000000000000 --- a/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`visualize loader pipeline helpers: build pipeline buildPipeline calls toExpression on vis_type if it exists 1`] = `"kibana | kibana_context | test"`; diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.test.ts b/src/plugins/visualizations/public/legacy/build_pipeline.test.ts deleted file mode 100644 index a3d867380602d..0000000000000 --- a/src/plugins/visualizations/public/legacy/build_pipeline.test.ts +++ /dev/null @@ -1,86 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { prepareJson, prepareString, buildPipeline } from './build_pipeline'; -import { Vis } from '..'; -import { dataPluginMock } from '../../../../plugins/data/public/mocks'; -import { parseExpression } from '../../../expressions/common'; - -describe('visualize loader pipeline helpers: build pipeline', () => { - describe('prepareJson', () => { - it('returns a correctly formatted key/value string', () => { - const expected = `foo='{}' `; // trailing space is expected - const actual = prepareJson('foo', {}); - expect(actual).toBe(expected); - }); - - it('stringifies provided data', () => { - const expected = `foo='{\"well\":\"hello\",\"there\":{\"friend\":true}}' `; - const actual = prepareJson('foo', { well: 'hello', there: { friend: true } }); - expect(actual).toBe(expected); - }); - - it('escapes single quotes', () => { - const expected = `foo='{\"well\":\"hello \\'hi\\'\",\"there\":{\"friend\":true}}' `; - const actual = prepareJson('foo', { well: `hello 'hi'`, there: { friend: true } }); - expect(actual).toBe(expected); - }); - - it('returns empty string if data is undefined', () => { - const actual = prepareJson('foo', undefined); - expect(actual).toBe(''); - }); - }); - - describe('prepareString', () => { - it('returns a correctly formatted key/value string', () => { - const expected = `foo='bar' `; // trailing space is expected - const actual = prepareString('foo', 'bar'); - expect(actual).toBe(expected); - }); - - it('escapes single quotes', () => { - const expected = `foo='\\'bar\\'' `; - const actual = prepareString('foo', `'bar'`); - expect(actual).toBe(expected); - }); - - it('returns empty string if data is undefined', () => { - const actual = prepareString('foo', undefined); - expect(actual).toBe(''); - }); - }); - - describe('buildPipeline', () => { - const dataStart = dataPluginMock.createStartContract(); - - it('calls toExpression on vis_type if it exists', async () => { - const vis = ({ - getState: () => {}, - isHierarchical: () => false, - data: { - aggs: { - getResponseAggs: () => [], - }, - searchSource: { - getField: jest.fn(), - getParent: jest.fn(), - }, - }, - // @ts-ignore - type: { - toExpressionAst: () => parseExpression('test'), - }, - } as unknown) as Vis; - const expression = await buildPipeline(vis, { - timefilter: dataStart.query.timefilter.timefilter, - }); - expect(expression).toMatchSnapshot(); - }); - }); -}); diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.ts b/src/plugins/visualizations/public/legacy/build_pipeline.ts deleted file mode 100644 index d337ef7bcf379..0000000000000 --- a/src/plugins/visualizations/public/legacy/build_pipeline.ts +++ /dev/null @@ -1,253 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { - buildExpression, - formatExpression, - SerializedFieldFormat, -} from '../../../../plugins/expressions/public'; -import { IAggConfig, search, TimefilterContract } from '../../../../plugins/data/public'; -import { Vis } from '../types'; -const { isDateHistogramBucketAggConfig } = search.aggs; - -interface SchemaConfigParams { - precision?: number; - useGeocentroid?: boolean; -} - -export interface SchemaConfig { - accessor: number; - label: string; - format: SerializedFieldFormat; - params: SchemaConfigParams; - aggType: string; -} - -export interface Schemas { - metric: SchemaConfig[]; - bucket?: SchemaConfig[]; - geo_centroid?: any[]; - group?: any[]; - params?: any[]; - radius?: any[]; - segment?: any[]; - split_column?: SchemaConfig[]; - split_row?: SchemaConfig[]; - width?: any[]; - // catch all for schema name - [key: string]: any[] | undefined; -} -export interface BuildPipelineParams { - timefilter: TimefilterContract; - timeRange?: any; - abortSignal?: AbortSignal; -} - -export const getSchemas = ( - vis: Vis, - { timeRange, timefilter }: BuildPipelineParams -): Schemas => { - const createSchemaConfig = (accessor: number, agg: IAggConfig): SchemaConfig => { - if (isDateHistogramBucketAggConfig(agg)) { - agg.params.timeRange = timeRange; - const bounds = - agg.params.timeRange && agg.fieldIsTimeField() - ? timefilter.calculateBounds(agg.params.timeRange) - : undefined; - agg.buckets.setBounds(bounds); - agg.buckets.setInterval(agg.params.interval); - } - - const hasSubAgg = [ - 'derivative', - 'moving_avg', - 'serial_diff', - 'cumulative_sum', - 'sum_bucket', - 'avg_bucket', - 'min_bucket', - 'max_bucket', - ].includes(agg.type.name); - - const formatAgg = hasSubAgg - ? agg.params.customMetric || agg.aggConfigs.getRequestAggById(agg.params.metricAgg) - : agg; - - const params: SchemaConfigParams = {}; - - if (agg.type.name === 'geohash_grid') { - params.precision = agg.params.precision; - params.useGeocentroid = agg.params.useGeocentroid; - } - - const label = agg.makeLabel && agg.makeLabel(); - - return { - accessor, - format: formatAgg.toSerializedFieldFormat(), - params, - label, - aggType: agg.type.name, - }; - }; - - let cnt = 0; - const schemas: Schemas = { - metric: [], - }; - - if (!vis.data.aggs) { - return schemas; - } - - const responseAggs = vis.data.aggs.getResponseAggs().filter((agg: IAggConfig) => agg.enabled); - const isHierarchical = vis.isHierarchical(); - const metrics = responseAggs.filter((agg: IAggConfig) => agg.type.type === 'metrics'); - responseAggs.forEach((agg: IAggConfig) => { - let skipMetrics = false; - let schemaName = agg.schema; - if (!schemaName) { - if (agg.type.name === 'geo_centroid') { - schemaName = 'geo_centroid'; - } else { - cnt++; - return; - } - } - if (schemaName === 'split') { - // TODO: We should check if there's a better way then casting to `any` here - schemaName = `split_${(vis.params as any).row ? 'row' : 'column'}`; - skipMetrics = responseAggs.length - metrics.length > 1; - } - if (!schemas[schemaName]) { - schemas[schemaName] = []; - } - if (!isHierarchical || agg.type.type !== 'metrics') { - schemas[schemaName]!.push(createSchemaConfig(cnt++, agg)); - } - if (isHierarchical && (agg.type.type !== 'metrics' || metrics.length === responseAggs.length)) { - metrics.forEach((metric: any) => { - const schemaConfig = createSchemaConfig(cnt++, metric); - if (!skipMetrics) { - schemas.metric.push(schemaConfig); - } - }); - } - }); - return schemas; -}; - -export const prepareJson = (variable: string, data?: object): string => { - if (data === undefined) { - return ''; - } - return `${variable}='${JSON.stringify(data).replace(/\\/g, `\\\\`).replace(/'/g, `\\'`)}' `; -}; - -export const escapeString = (data: string): string => { - return data.replace(/\\/g, `\\\\`).replace(/'/g, `\\'`); -}; - -export const prepareString = (variable: string, data?: string): string => { - if (data === undefined) { - return ''; - } - return `${variable}='${escapeString(data)}' `; -}; - -export const prepareValue = (variable: string, data: any, raw: boolean = false) => { - if (data === undefined) { - return ''; - } - if (raw) { - return `${variable}=${data} `; - } - switch (typeof data) { - case 'string': - return prepareString(variable, data); - case 'object': - return prepareJson(variable, data); - default: - return `${variable}=${data} `; - } -}; - -export const prepareDimension = (variable: string, data: any) => { - if (data === undefined) { - return ''; - } - - let expr = `${variable}={visdimension ${data.accessor} `; - if (data.format) { - expr += prepareValue('format', data.format.id); - expr += prepareJson('formatParams', data.format.params); - } - expr += '} '; - - return expr; -}; - -export const buildPipeline = async (vis: Vis, params: BuildPipelineParams) => { - const { indexPattern, searchSource } = vis.data; - const query = searchSource!.getField('query'); - const filters = searchSource!.getField('filter'); - const { uiState, title } = vis; - - // context - let pipeline = `kibana | kibana_context `; - if (query) { - pipeline += prepareJson('query', query); - } - if (filters) { - pipeline += prepareJson('filters', filters); - } - if (vis.data.savedSearchId) { - pipeline += prepareString('savedSearchId', vis.data.savedSearchId); - } - pipeline += '| '; - - if (vis.type.toExpressionAst) { - const visAst = await vis.type.toExpressionAst(vis, params); - pipeline += formatExpression(visAst); - } else { - // request handler - if (vis.type.requestHandler === 'courier') { - pipeline += `esaggs - index={indexPatternLoad ${prepareString('id', indexPattern!.id)}} - metricsAtAllLevels=${vis.isHierarchical()} - partialRows=${vis.params.showPartialRows || false} `; - if (vis.data.aggs) { - vis.data.aggs.aggs.forEach((agg) => { - const ast = agg.toExpressionAst(); - if (ast) { - pipeline += `aggs={${buildExpression(ast).toString()}} `; - } - }); - } - pipeline += `| `; - } else { - const schemas = getSchemas(vis, params); - const visConfig = { ...vis.params }; - visConfig.dimensions = schemas; - visConfig.title = title; - pipeline += `visualization type='${vis.type.name}' - ${prepareJson('visConfig', visConfig)} - ${prepareJson('uiState', uiState)} - metricsAtAllLevels=${vis.isHierarchical()} - partialRows=${vis.params.showPartialRows || false} `; - if (indexPattern) { - pipeline += `${prepareString('index', indexPattern.id)} `; - if (vis.data.aggs) { - pipeline += `${prepareJson('aggConfigs', vis.data.aggs!.aggs)}`; - } - } - } - } - - return pipeline; -}; diff --git a/src/plugins/visualizations/public/mocks.ts b/src/plugins/visualizations/public/mocks.ts index a8bf3b1ff3eb9..4f9fd53125847 100644 --- a/src/plugins/visualizations/public/mocks.ts +++ b/src/plugins/visualizations/public/mocks.ts @@ -22,7 +22,6 @@ import { savedObjectsPluginMock } from '../../../plugins/saved_objects/public/mo const createSetupContract = (): VisualizationsSetup => ({ createBaseVisualization: jest.fn(), - createReactVisualization: jest.fn(), registerAlias: jest.fn(), hideTypes: jest.fn(), }); diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index 8a82b36f37caf..24514dde10cda 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -20,15 +20,12 @@ import { TypesService, TypesSetup, TypesStart } from './vis_types'; import { setUISettings, setTypes, - setI18n, setApplication, setCapabilities, setHttp, - setIndexPatterns, setSearch, setSavedObjects, setUsageCollector, - setFilterManager, setExpressions, setUiActions, setSavedVisualizationsLoader, @@ -47,8 +44,6 @@ import { } from './embeddable'; import { ExpressionsSetup, ExpressionsStart } from '../../expressions/public'; import { EmbeddableSetup, EmbeddableStart } from '../../embeddable/public'; -import { visualization as visualizationFunction } from './expressions/visualization_function'; -import { visualization as visualizationRenderer } from './expressions/visualization_renderer'; import { range as rangeExpressionFunction } from './expression_functions/range'; import { visDimension as visDimensionExpressionFunction } from './expression_functions/vis_dimension'; import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../plugins/data/public'; @@ -138,8 +133,6 @@ export class VisualizationsPlugin setUISettings(core.uiSettings); setUsageCollector(usageCollection); - expressions.registerFunction(visualizationFunction); - expressions.registerRenderer(visualizationRenderer); expressions.registerFunction(rangeExpressionFunction); expressions.registerFunction(visDimensionExpressionFunction); const embeddableFactory = new VisualizeEmbeddableFactory({ start }); @@ -155,7 +148,6 @@ export class VisualizationsPlugin { data, expressions, uiActions, embeddable, dashboard, savedObjects }: VisualizationsStartDeps ): VisualizationsStart { const types = this.types.start(); - setI18n(core.i18n); setTypes(types); setEmbeddable(embeddable); setApplication(core.application); @@ -163,9 +155,7 @@ export class VisualizationsPlugin setHttp(core.http); setSavedObjects(core.savedObjects); setDocLinks(core.docLinks); - setIndexPatterns(data.indexPatterns); setSearch(data.search); - setFilterManager(data.query.filterManager); setExpressions(expressions); setUiActions(uiActions); setTimeFilter(data.query.timefilter.timefilter); diff --git a/src/plugins/visualizations/public/services.ts b/src/plugins/visualizations/public/services.ts index dd7d0941e7378..30cc514e48de4 100644 --- a/src/plugins/visualizations/public/services.ts +++ b/src/plugins/visualizations/public/services.ts @@ -11,7 +11,6 @@ import { Capabilities, ChromeStart, HttpStart, - I18nStart, IUiSettingsClient, OverlayStart, SavedObjectsStart, @@ -19,12 +18,7 @@ import { } from '../../../core/public'; import { TypesStart } from './vis_types'; import { createGetterSetter } from '../../../plugins/kibana_utils/public'; -import { - DataPublicPluginStart, - FilterManager, - IndexPatternsContract, - TimefilterContract, -} from '../../../plugins/data/public'; +import { DataPublicPluginStart, TimefilterContract } from '../../../plugins/data/public'; import { UsageCollectionSetup } from '../../../plugins/usage_collection/public'; import { ExpressionsStart } from '../../../plugins/expressions/public'; import { UiActionsStart } from '../../../plugins/ui_actions/public'; @@ -48,20 +42,10 @@ export const [getSavedObjects, setSavedObjects] = createGetterSetter('Types'); -export const [getI18n, setI18n] = createGetterSetter('I18n'); - export const [getDocLinks, setDocLinks] = createGetterSetter('DocLinks'); -export const [getFilterManager, setFilterManager] = createGetterSetter( - 'FilterManager' -); - export const [getTimeFilter, setTimeFilter] = createGetterSetter('TimeFilter'); -export const [getIndexPatterns, setIndexPatterns] = createGetterSetter( - 'IndexPatterns' -); - export const [getSearch, setSearch] = createGetterSetter('Search'); export const [getUsageCollector, setUsageCollector] = createGetterSetter( diff --git a/src/plugins/visualizations/public/types.ts b/src/plugins/visualizations/public/types.ts index dc9ca49840561..3a751c92f3ae7 100644 --- a/src/plugins/visualizations/public/types.ts +++ b/src/plugins/visualizations/public/types.ts @@ -7,26 +7,27 @@ */ import { SavedObject } from '../../../plugins/saved_objects/public'; -import { SearchSourceFields, TimefilterContract } from '../../../plugins/data/public'; +import { + AggConfigOptions, + IAggConfigs, + SearchSourceFields, + TimefilterContract, +} from '../../../plugins/data/public'; import { ExpressionAstExpression } from '../../expressions/public'; import { SerializedVis, Vis } from './vis'; -import { ExprVis } from './expressions/vis'; -import { SavedVisState, VisParams } from '../common/types'; +import { PersistedState } from './persisted_state'; +import { VisParams } from '../common'; export { Vis, SerializedVis, VisParams }; -export interface VisualizationController { - render(visData: any, visParams: any): Promise; - destroy(): void; - isLoaded?(): Promise | void; +export interface SavedVisState { + title: string; + type: string; + params: VisParams; + aggs: AggConfigOptions[]; } -export type VisualizationControllerConstructor = new ( - el: HTMLElement, - vis: ExprVis -) => VisualizationController; - export interface ISavedVis { id?: string; title: string; @@ -40,13 +41,6 @@ export interface ISavedVis { export interface VisSavedObject extends SavedObject, ISavedVis {} -export interface VisResponseValue { - visType: string; - visData: object; - visConfig: object; - params?: object; -} - export interface VisToExpressionAstParams { timefilter: TimefilterContract; timeRange?: any; @@ -57,3 +51,15 @@ export type VisToExpressionAst = ( vis: Vis, params: VisToExpressionAstParams ) => Promise | ExpressionAstExpression; + +export interface VisEditorOptionsProps { + aggs: IAggConfigs; + hasHistogramAgg: boolean; + isTabSelected: boolean; + stateParams: VisParamType; + vis: Vis; + uiState: PersistedState; + setValue(paramName: T, value: VisParamType[T]): void; + setValidity(isValid: boolean): void; + setTouched(isTouched: boolean): void; +} diff --git a/src/plugins/visualizations/public/vis.ts b/src/plugins/visualizations/public/vis.ts index 56a151fb82ed3..ab44c361bd5bd 100644 --- a/src/plugins/visualizations/public/vis.ts +++ b/src/plugins/visualizations/public/vis.ts @@ -22,7 +22,6 @@ import { i18n } from '@kbn/i18n'; import { PersistedState } from './persisted_state'; import { getTypes, getAggs, getSearch, getSavedSearchLoader } from './services'; -import { VisType } from './vis_types'; import { IAggConfigs, IndexPattern, @@ -30,6 +29,7 @@ import { AggConfigOptions, SearchSourceFields, } from '../../../plugins/data/public'; +import { BaseVisType } from './vis_types'; import { VisParams } from '../common/types'; export interface SerializedVisData { @@ -71,14 +71,11 @@ const getSearchSource = async (inputSearchSource: ISearchSource, savedSearchId?: type PartialVisState = Assign }>; export class Vis { - public readonly type: VisType; + public readonly type: BaseVisType; public readonly id?: string; public title: string = ''; public description: string = ''; public params: TVisParams; - // Session state is for storing information that is transitory, and will not be saved with the visualization. - // For instance, map bounds, which depends on the view port, browser window size, etc. - public sessionState: Record = {}; public data: VisData = {}; public readonly uiState: PersistedState; diff --git a/src/plugins/visualizations/public/vis_schemas.ts b/src/plugins/visualizations/public/vis_schemas.ts new file mode 100644 index 0000000000000..a00417b90baac --- /dev/null +++ b/src/plugins/visualizations/public/vis_schemas.ts @@ -0,0 +1,136 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { SerializedFieldFormat } from '../../expressions/public'; +import { IAggConfig, search } from '../../data/public'; + +import { Vis, VisToExpressionAstParams } from './types'; + +const { isDateHistogramBucketAggConfig } = search.aggs; + +interface SchemaConfigParams { + precision?: number; + useGeocentroid?: boolean; +} + +export interface SchemaConfig { + accessor: number; + label: string; + format: SerializedFieldFormat; + params: SchemaConfigParams; + aggType: string; +} + +export interface Schemas { + metric: SchemaConfig[]; + bucket?: SchemaConfig[]; + geo_centroid?: any[]; + group?: any[]; + params?: any[]; + radius?: any[]; + segment?: any[]; + split_column?: SchemaConfig[]; + split_row?: SchemaConfig[]; + width?: any[]; + // catch all for schema name + [key: string]: any[] | undefined; +} + +export const getVisSchemas = ( + vis: Vis, + { timeRange, timefilter }: VisToExpressionAstParams +): Schemas => { + const createSchemaConfig = (accessor: number, agg: IAggConfig): SchemaConfig => { + if (isDateHistogramBucketAggConfig(agg)) { + agg.params.timeRange = timeRange; + const bounds = + agg.params.timeRange && agg.fieldIsTimeField() + ? timefilter.calculateBounds(agg.params.timeRange) + : undefined; + agg.buckets.setBounds(bounds); + agg.buckets.setInterval(agg.params.interval); + } + + const hasSubAgg = [ + 'derivative', + 'moving_avg', + 'serial_diff', + 'cumulative_sum', + 'sum_bucket', + 'avg_bucket', + 'min_bucket', + 'max_bucket', + ].includes(agg.type.name); + + const formatAgg = hasSubAgg + ? agg.params.customMetric || agg.aggConfigs.getRequestAggById(agg.params.metricAgg) + : agg; + + const params: SchemaConfigParams = {}; + + if (agg.type.name === 'geohash_grid') { + params.precision = agg.params.precision; + params.useGeocentroid = agg.params.useGeocentroid; + } + + const label = agg.makeLabel && agg.makeLabel(); + + return { + accessor, + format: formatAgg.toSerializedFieldFormat(), + params, + label, + aggType: agg.type.name, + }; + }; + + let cnt = 0; + const schemas: Schemas = { + metric: [], + }; + + if (!vis.data.aggs) { + return schemas; + } + + const responseAggs = vis.data.aggs.getResponseAggs().filter((agg: IAggConfig) => agg.enabled); + const isHierarchical = vis.isHierarchical(); + const metrics = responseAggs.filter((agg: IAggConfig) => agg.type.type === 'metrics'); + responseAggs.forEach((agg: IAggConfig) => { + let skipMetrics = false; + let schemaName = agg.schema; + if (!schemaName) { + if (agg.type.name === 'geo_centroid') { + schemaName = 'geo_centroid'; + } else { + cnt++; + return; + } + } + if (schemaName === 'split') { + // TODO: We should check if there's a better way then casting to `any` here + schemaName = `split_${(vis.params as any).row ? 'row' : 'column'}`; + skipMetrics = responseAggs.length - metrics.length > 1; + } + if (!schemas[schemaName]) { + schemas[schemaName] = []; + } + if (!isHierarchical || agg.type.type !== 'metrics') { + schemas[schemaName]!.push(createSchemaConfig(cnt++, agg)); + } + if (isHierarchical && (agg.type.type !== 'metrics' || metrics.length === responseAggs.length)) { + metrics.forEach((metric: any) => { + const schemaConfig = createSchemaConfig(cnt++, metric); + if (!skipMetrics) { + schemas.metric.push(schemaConfig); + } + }); + } + }); + return schemas; +}; diff --git a/src/plugins/visualizations/public/vis_types/base_vis_type.test.ts b/src/plugins/visualizations/public/vis_types/base_vis_type.test.ts index 58670545f53e7..5091f9aca6b69 100644 --- a/src/plugins/visualizations/public/vis_types/base_vis_type.test.ts +++ b/src/plugins/visualizations/public/vis_types/base_vis_type.test.ts @@ -16,7 +16,16 @@ describe('BaseVisType', () => { name: 'test', title: 'test', description: 'test', - visualization: {} as any, + visConfig: { + defaults: {}, + }, + toExpressionAst: () => ({ + type: 'expression', + chain: [], + }), + editorConfig: { + editor: 'custom', + }, }); }).toThrow(); }); diff --git a/src/plugins/visualizations/public/vis_types/base_vis_type.ts b/src/plugins/visualizations/public/vis_types/base_vis_type.ts index e6e1704d45a8e..7425bf06bdfc2 100644 --- a/src/plugins/visualizations/public/vis_types/base_vis_type.ts +++ b/src/plugins/visualizations/public/vis_types/base_vis_type.ts @@ -9,54 +9,9 @@ import { defaultsDeep } from 'lodash'; import { VisParams } from '../types'; -import { VisType, VisTypeOptions, VisGroups } from './types'; +import { VisTypeDefinition, VisTypeOptions, VisGroups } from './types'; import { Schemas } from './schemas'; -interface CommonBaseVisTypeOptions - extends Pick< - VisType, - | 'description' - | 'getInfoMessage' - | 'getSupportedTriggers' - | 'hierarchicalData' - | 'icon' - | 'image' - | 'inspectorAdapters' - | 'name' - | 'requestHandler' - | 'responseHandler' - | 'setup' - | 'title' - >, - Pick< - Partial>, - | 'editorConfig' - | 'hidden' - | 'stage' - | 'getUsedIndexPattern' - | 'useCustomNoDataScreen' - | 'visConfig' - | 'group' - | 'titleInWizard' - | 'note' - > { - options?: Partial['options']>; -} - -interface ExpressionBaseVisTypeOptions extends CommonBaseVisTypeOptions { - toExpressionAst: VisType['toExpressionAst']; - visualization?: undefined; -} - -interface VisualizationBaseVisTypeOptions extends CommonBaseVisTypeOptions { - toExpressionAst?: undefined; - visualization: VisType['visualization']; -} - -export type BaseVisTypeOptions = - | ExpressionBaseVisTypeOptions - | VisualizationBaseVisTypeOptions; - const defaultOptions: VisTypeOptions = { showTimePicker: true, showQueryBar: true, @@ -65,7 +20,7 @@ const defaultOptions: VisTypeOptions = { hierarchicalData: false, // we should get rid of this i guess ? }; -export class BaseVisType implements VisType { +export class BaseVisType { public readonly name; public readonly title; public readonly description; @@ -76,23 +31,20 @@ export class BaseVisType implements VisType public readonly stage; public readonly group; public readonly titleInWizard; - public readonly options; - public readonly visualization; + public readonly options: VisTypeOptions; public readonly visConfig; public readonly editorConfig; public hidden; - public readonly requestHandler; - public readonly responseHandler; + public readonly requiresSearch; public readonly hierarchicalData; public readonly setup; public readonly getUsedIndexPattern; - public readonly useCustomNoDataScreen; public readonly inspectorAdapters; public readonly toExpressionAst; public readonly getInfoMessage; public readonly schemas; - constructor(opts: BaseVisTypeOptions) { + constructor(opts: VisTypeDefinition) { if (!opts.icon && !opts.image) { throw new Error('vis_type must define its icon or image'); } @@ -104,7 +56,6 @@ export class BaseVisType implements VisType this.title = opts.title; this.icon = opts.icon; this.image = opts.image; - this.visualization = opts.visualization; this.visConfig = defaultsDeep({}, opts.visConfig, { defaults: {} }); this.editorConfig = defaultsDeep({}, opts.editorConfig, { collections: {} }); this.options = defaultsDeep({}, opts.options, defaultOptions); @@ -112,20 +63,14 @@ export class BaseVisType implements VisType this.group = opts.group ?? VisGroups.AGGBASED; this.titleInWizard = opts.titleInWizard ?? ''; this.hidden = opts.hidden ?? false; - this.requestHandler = opts.requestHandler ?? 'courier'; - this.responseHandler = opts.responseHandler ?? 'none'; + this.requiresSearch = opts.requiresSearch ?? false; this.setup = opts.setup; this.hierarchicalData = opts.hierarchicalData ?? false; this.getUsedIndexPattern = opts.getUsedIndexPattern; - this.useCustomNoDataScreen = opts.useCustomNoDataScreen ?? false; this.inspectorAdapters = opts.inspectorAdapters; this.toExpressionAst = opts.toExpressionAst; this.getInfoMessage = opts.getInfoMessage; this.schemas = new Schemas(this.editorConfig?.schemas ?? []); } - - public get requiresSearch(): boolean { - return this.requestHandler !== 'none'; - } } diff --git a/src/plugins/visualizations/public/vis_types/index.ts b/src/plugins/visualizations/public/vis_types/index.ts index 0b3b1ec75cbe3..021c55e7a14bc 100644 --- a/src/plugins/visualizations/public/vis_types/index.ts +++ b/src/plugins/visualizations/public/vis_types/index.ts @@ -9,6 +9,5 @@ export * from './types_service'; export { Schemas } from './schemas'; export { VisGroups } from './types'; -export type { VisType, ISchemas, Schema } from './types'; -export type { BaseVisTypeOptions } from './base_vis_type'; -export type { ReactVisTypeOptions } from './react_vis_type'; +export { BaseVisType } from './base_vis_type'; +export type { VisTypeDefinition, ISchemas, Schema } from './types'; diff --git a/src/plugins/visualizations/public/vis_types/react_vis_controller.tsx b/src/plugins/visualizations/public/vis_types/react_vis_controller.tsx deleted file mode 100644 index 91e8db947f7e4..0000000000000 --- a/src/plugins/visualizations/public/vis_types/react_vis_controller.tsx +++ /dev/null @@ -1,46 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { VisualizationController } from '../types'; -import { getI18n, getUISettings } from '../services'; -import { ExprVis } from '../expressions/vis'; - -export class ReactVisController implements VisualizationController { - constructor(private element: HTMLElement, private vis: ExprVis) {} - - public render(visData: any, visParams: any): Promise { - const I18nContext = getI18n().Context; - - return new Promise((resolve, reject) => { - if (!this.vis.type || !this.vis.type.visConfig || !this.vis.type.visConfig.component) { - reject('Missing component for ReactVisType'); - } - - const Component = this.vis.type.visConfig.component; - const config = getUISettings(); - render( - - - , - this.element - ); - }); - } - - public destroy() { - unmountComponentAtNode(this.element); - } -} diff --git a/src/plugins/visualizations/public/vis_types/react_vis_type.test.ts b/src/plugins/visualizations/public/vis_types/react_vis_type.test.ts deleted file mode 100644 index 76d3321f2c92d..0000000000000 --- a/src/plugins/visualizations/public/vis_types/react_vis_type.test.ts +++ /dev/null @@ -1,36 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { cloneDeep } from 'lodash'; -import { ReactVisType } from './react_vis_type'; - -describe('React Vis Type', () => { - const visConfig = { - name: 'test', - title: 'test', - description: 'test', - icon: 'test', - visConfig: { component: 'test' }, - }; - - describe('initialization', () => { - it('should throw if component is not set', () => { - expect(() => { - const missingConfig = cloneDeep(visConfig); - // @ts-expect-error TS knows it's a required property - delete missingConfig.visConfig.component; - new ReactVisType(missingConfig); - }).toThrow(); - }); - - it('creates react controller', () => { - const visType = new ReactVisType(visConfig); - expect(visType.visualization).not.toBeUndefined(); - }); - }); -}); diff --git a/src/plugins/visualizations/public/vis_types/react_vis_type.ts b/src/plugins/visualizations/public/vis_types/react_vis_type.ts deleted file mode 100644 index 336599f67702d..0000000000000 --- a/src/plugins/visualizations/public/vis_types/react_vis_type.ts +++ /dev/null @@ -1,35 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { BaseVisType, BaseVisTypeOptions } from './base_vis_type'; -import { ReactVisController } from './react_vis_controller'; -import { VisType } from './types'; - -export type ReactVisTypeOptions = Omit< - BaseVisTypeOptions, - 'visualization' | 'toExpressionAst' ->; - -/** - * This class should only be used for visualizations not using the `toExpressionAst` with a custom renderer. - * If you implement a custom renderer you should just mount a react component inside this. - */ -export class ReactVisType - extends BaseVisType - implements VisType { - constructor(opts: ReactVisTypeOptions) { - super({ - ...opts, - visualization: ReactVisController, - }); - - if (!this.visConfig.component) { - throw new Error('Missing component for ReactVisType'); - } - } -} diff --git a/src/plugins/visualizations/public/vis_types/schemas.ts b/src/plugins/visualizations/public/vis_types/schemas.ts index 3ce38e68e7f32..50bf50a857815 100644 --- a/src/plugins/visualizations/public/vis_types/schemas.ts +++ b/src/plugins/visualizations/public/vis_types/schemas.ts @@ -37,7 +37,6 @@ export class Schemas implements ISchemas { group: AggGroupNames.Buckets, title: schema.name, aggFilter: '*', - editor: false, params: [], }); diff --git a/src/plugins/visualizations/public/vis_types/types.ts b/src/plugins/visualizations/public/vis_types/types.ts index b38c9e41d5c7b..7244aae64e963 100644 --- a/src/plugins/visualizations/public/vis_types/types.ts +++ b/src/plugins/visualizations/public/vis_types/types.ts @@ -7,10 +7,10 @@ */ import { IconType } from '@elastic/eui'; -import React, { ReactNode } from 'react'; +import { ReactNode } from 'react'; import { Adapters } from 'src/plugins/inspector'; import { IndexPattern, AggGroupNames, AggParam, AggGroupName } from '../../../data/public'; -import { Vis, VisParams, VisToExpressionAst, VisualizationControllerConstructor } from '../types'; +import { Vis, VisEditorOptionsProps, VisParams, VisToExpressionAst } from '../types'; export interface VisTypeOptions { showTimePicker: boolean; @@ -34,7 +34,6 @@ export interface ISchemas { export interface Schema { aggFilter: string[]; - editor: boolean | string; group: AggGroupName; max: number; min: number; @@ -49,11 +48,35 @@ export interface Schema { tooltip?: ReactNode; } +type DefaultEditorOptionsComponent = React.ComponentType< + VisEditorOptionsProps +>; + +interface DefaultEditorConfig { + // collections should moved directly into default editor in https://github.com/elastic/kibana/issues/84879 + collections?: { + [key: string]: Array<{ text: string; value: string }> | Array<{ id: string; label: string }>; + }; + enableAutoApply?: boolean; + defaultSize?: string; + optionsTemplate?: DefaultEditorOptionsComponent; + optionTabs?: Array<{ + name: string; + title: string; + editor: DefaultEditorOptionsComponent; + }>; + schemas?: Array>; +} + +interface CustomEditorConfig { + editor: string; +} + /** - * A visualization type representing one specific type of "classical" + * A visualization type definition representing a spec of one specific type of "classical" * visualizations (i.e. not Lens visualizations). */ -export interface VisType { +export interface VisTypeDefinition { /** * Visualization unique name */ @@ -69,7 +92,7 @@ export interface VisType { /** * If given, it will be diplayed on the wizard vis card as a note in italic. */ - readonly note: string; + readonly note?: string; /** * If given, it will return the supported triggers for this vis. */ @@ -82,8 +105,6 @@ export interface VisType { readonly getUsedIndexPattern?: (visParams: VisParams) => IndexPattern[] | Promise; readonly isAccessible?: boolean; - readonly requestHandler?: string | unknown; - readonly responseHandler?: string | unknown; /** * It is the visualization icon, displayed on the wizard. */ @@ -94,21 +115,27 @@ export interface VisType { readonly image?: string; /** * Describes the visualization stage + * @default 'production' */ - readonly stage: 'experimental' | 'beta' | 'production'; + readonly stage?: 'experimental' | 'beta' | 'production'; /** * Describes the experience group that the visualization belongs. * It can be on tools, aggregation based or promoted group. + * @default 'aggbased' */ - readonly group: VisGroups; + readonly group?: VisGroups; /** * If given, it will be displayed on the wizard instead of the title. * We use it because we want to differentiate the vis title from the * way it is presented on the wizard */ - readonly titleInWizard: string; - readonly requiresSearch: boolean; - readonly useCustomNoDataScreen: boolean; + readonly titleInWizard?: string; + /** + * The flag is necessary for aggregation based visualizations. + * When "true", an additional step on the vis creation wizard will be provided + * with the selection of a search source - an index pattern or a saved search. + */ + readonly requiresSearch?: boolean; readonly hierarchicalData?: boolean | ((vis: { params: TVisParams }) => boolean); readonly inspectorAdapters?: Adapters | (() => Adapters); /** @@ -118,18 +145,27 @@ export interface VisType { * of this type. */ readonly getInfoMessage?: (vis: Vis) => React.ReactNode; - - readonly toExpressionAst?: VisToExpressionAst; - readonly visualization?: VisualizationControllerConstructor; + /** + * Should be provided to expand base visualization expression with + * custom exprsesion chain, including render expression. + * Explicit renderer should be registered in expressions plugin to render your visualization. + */ + readonly toExpressionAst: VisToExpressionAst; readonly setup?: (vis: Vis) => Promise>; - hidden: boolean; + hidden?: boolean; - readonly schemas: ISchemas; + readonly options?: Partial; - readonly options: VisTypeOptions; - - // TODO: The following types still need to be refined properly. - readonly editorConfig: Record; + /** + * Config for the default editor. + * Custom editor can be specified. + */ + readonly editorConfig: DefaultEditorConfig | CustomEditorConfig; + /** + * Have the "defaults" prop with default params for a visualization. + * TODO: ideally should have next type: { defaults: TVisParams } , but currently + * have incosistencies in legacy visLib visualizations + */ readonly visConfig: Record; } diff --git a/src/plugins/visualizations/public/vis_types/types_service.ts b/src/plugins/visualizations/public/vis_types/types_service.ts index d26f5ca882b65..09f7f8f599f2c 100644 --- a/src/plugins/visualizations/public/vis_types/types_service.ts +++ b/src/plugins/visualizations/public/vis_types/types_service.ts @@ -7,9 +7,8 @@ */ import { visTypeAliasRegistry, VisTypeAlias } from './vis_type_alias_registry'; -import { BaseVisType, BaseVisTypeOptions } from './base_vis_type'; -import { ReactVisType, ReactVisTypeOptions } from './react_vis_type'; -import { VisType, VisGroups } from './types'; +import { BaseVisType } from './base_vis_type'; +import { VisTypeDefinition, VisGroups } from './types'; /** * Vis Types Service @@ -17,10 +16,10 @@ import { VisType, VisGroups } from './types'; * @internal */ export class TypesService { - private types: Record> = {}; + private types: Record> = {}; private unregisteredHiddenTypes: string[] = []; - private registerVisualization(visDefinition: VisType) { + private registerVisualization(visDefinition: BaseVisType) { if (this.unregisteredHiddenTypes.includes(visDefinition.name)) { visDefinition.hidden = true; } @@ -37,18 +36,10 @@ export class TypesService { * registers a visualization type * @param config - visualization type definition */ - createBaseVisualization: (config: BaseVisTypeOptions): void => { + createBaseVisualization: (config: VisTypeDefinition): void => { const vis = new BaseVisType(config); this.registerVisualization(vis); }, - /** - * registers a visualization which uses react for rendering - * @param config - visualization type definition - */ - createReactVisualization: (config: ReactVisTypeOptions): void => { - const vis = new ReactVisType(config); - this.registerVisualization(vis); - }, /** * registers a visualization alias * alias is a visualization type without implementation, it just redirects somewhere in kibana @@ -77,13 +68,13 @@ export class TypesService { * returns specific visualization or undefined if not found * @param {string} visualization - id of visualization to return */ - get: (visualization: string): VisType => { + get: (visualization: string): BaseVisType | undefined => { return this.types[visualization]; }, /** * returns all registered visualization types */ - all: (): VisType[] => { + all: (): BaseVisType[] => { return [...Object.values(this.types)]; }, /** @@ -119,6 +110,3 @@ export type TypesStart = ReturnType; /** @public types */ export { VisTypeAlias }; - -/** @public static code */ -// TODO once items are moved from ui/vis into this service diff --git a/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.test.tsx b/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.test.tsx index 053d36e89ad75..9e825982b9065 100644 --- a/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.test.tsx +++ b/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.test.tsx @@ -8,19 +8,13 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; -import { TypesStart, VisType, VisGroups } from '../../vis_types'; +import { TypesStart, BaseVisType, VisGroups } from '../../vis_types'; import { AggBasedSelection } from './agg_based_selection'; describe('AggBasedSelection', () => { const defaultVisTypeParams = { hidden: false, - visualization: class Controller { - public render = jest.fn(); - public destroy = jest.fn(); - }, requiresSearch: false, - requestHandler: 'none', - responseHandler: 'none', }; const _visTypes = [ { @@ -50,22 +44,16 @@ describe('AggBasedSelection', () => { stage: 'production', ...defaultVisTypeParams, }, - ] as VisType[]; + ] as BaseVisType[]; const visTypes: TypesStart = { - get(id: string): VisType { - return _visTypes.find((vis) => vis.name === id) as VisType; - }, - all: () => { - return (_visTypes as unknown) as VisType[]; + get(id: string): BaseVisType { + return (_visTypes.find((vis) => vis.name === id) as unknown) as BaseVisType; }, + all: () => _visTypes, getAliases: () => [], unRegisterAlias: () => [], - getByGroup: (group: VisGroups) => { - return _visTypes.filter((type) => { - return type.group === group; - }) as VisType[]; - }, + getByGroup: (group: VisGroups) => _visTypes.filter((type) => type.group === group), }; beforeAll(() => { diff --git a/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.tsx b/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.tsx index 1496f0c60f82d..f5013dddc5c79 100644 --- a/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.tsx +++ b/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.tsx @@ -25,17 +25,17 @@ import { } from '@elastic/eui'; import { memoizeLast } from '../../legacy/memoize'; -import type { VisType, TypesStart } from '../../vis_types'; +import type { BaseVisType, TypesStart } from '../../vis_types'; import { VisGroups } from '../../vis_types'; import { DialogNavigation } from '../dialog_navigation'; interface VisTypeListEntry { - type: VisType; + type: BaseVisType; highlighted: boolean; } interface AggBasedSelectionProps { - onVisTypeSelected: (visType: VisType) => void; + onVisTypeSelected: (visType: BaseVisType) => void; visTypesRegistry: TypesStart; toggleGroups: (flag: boolean) => void; } diff --git a/src/plugins/visualizations/public/wizard/group_selection/group_selection.test.tsx b/src/plugins/visualizations/public/wizard/group_selection/group_selection.test.tsx index 60857cc39661e..74163296e31fd 100644 --- a/src/plugins/visualizations/public/wizard/group_selection/group_selection.test.tsx +++ b/src/plugins/visualizations/public/wizard/group_selection/group_selection.test.tsx @@ -8,20 +8,14 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; -import { TypesStart, VisType, VisGroups } from '../../vis_types'; +import { TypesStart, BaseVisType, VisGroups } from '../../vis_types'; import { GroupSelection } from './group_selection'; import { DocLinksStart } from '../../../../../core/public'; describe('GroupSelection', () => { const defaultVisTypeParams = { hidden: false, - visualization: class Controller { - public render = jest.fn(); - public destroy = jest.fn(); - }, requiresSearch: false, - requestHandler: 'none', - responseHandler: 'none', }; const _visTypes = [ { @@ -64,22 +58,22 @@ describe('GroupSelection', () => { aliasPath: '#/anotherUrl', promotion: true, } as unknown, - ] as VisType[]; + ] as BaseVisType[]; - const visTypesRegistry = (visTypes: VisType[]): TypesStart => { + const visTypesRegistry = (visTypes: BaseVisType[]): TypesStart => { return { - get(id: string): VisType { - return (visTypes.find((vis) => vis.name === id) as unknown) as VisType; + get(id: string): BaseVisType { + return (visTypes.find((vis) => vis.name === id) as unknown) as BaseVisType; }, all: () => { - return (visTypes as unknown) as VisType[]; + return (visTypes as unknown) as BaseVisType[]; }, getAliases: () => [], unRegisterAlias: () => [], getByGroup: (group: VisGroups) => { return (visTypes.filter((type) => { return type.group === group; - }) as unknown) as VisType[]; + }) as unknown) as BaseVisType[]; }, }; }; @@ -142,7 +136,7 @@ describe('GroupSelection', () => { }; const wrapper = mountWithIntl( { }; const wrapper = mountWithIntl( { }; const wrapper = mountWithIntl( { it('should sort promoted visualizations first', () => { const wrapper = mountWithIntl( { it('should render disabled aliases with a disabled class', () => { const wrapper = mountWithIntl( { it('should render a basic badge with link for disabled aliases with promoTooltip', () => { const wrapper = mountWithIntl( { }; const wrapper = mountWithIntl( { }; const wrapper = mountWithIntl( void; + onVisTypeSelected: (visType: BaseVisType | VisTypeAlias) => void; visTypesRegistry: TypesStart; docLinks: DocLinksStart; toggleGroups: (flag: boolean) => void; @@ -43,12 +43,12 @@ interface GroupSelectionProps { } interface VisCardProps { - onVisTypeSelected: (visType: VisType | VisTypeAlias) => void; - visType: VisType | VisTypeAlias; + onVisTypeSelected: (visType: BaseVisType | VisTypeAlias) => void; + visType: BaseVisType | VisTypeAlias; showExperimental?: boolean | undefined; } -function isVisTypeAlias(type: VisType | VisTypeAlias): type is VisTypeAlias { +function isVisTypeAlias(type: BaseVisType | VisTypeAlias): type is VisTypeAlias { return 'aliasPath' in type; } diff --git a/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx index baaed6d4ea8bf..c8a3c81f517ea 100644 --- a/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx @@ -8,21 +8,16 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; -import { TypesStart, VisType, VisGroups } from '../vis_types'; +import { TypesStart, VisGroups } from '../vis_types'; import NewVisModal from './new_vis_modal'; import { ApplicationStart, SavedObjectsStart, DocLinksStart } from '../../../../core/public'; import { embeddablePluginMock } from '../../../embeddable/public/mocks'; +import { BaseVisType } from '../vis_types'; describe('NewVisModal', () => { const defaultVisTypeParams = { hidden: false, - visualization: class Controller { - public render = jest.fn(); - public destroy = jest.fn(); - }, requiresSearch: false, - requestHandler: 'none', - responseHandler: 'none', }; const _visTypes = [ { @@ -61,21 +56,15 @@ describe('NewVisModal', () => { stage: 'production', ...defaultVisTypeParams, }, - ]; + ] as BaseVisType[]; const visTypes: TypesStart = { - get(id: string): VisType { - return (_visTypes.find((vis) => vis.name === id) as unknown) as VisType; - }, - all: () => { - return (_visTypes as unknown) as VisType[]; + get(id: string): BaseVisType { + return (_visTypes.find((vis) => vis.name === id) as unknown) as BaseVisType; }, + all: () => _visTypes, getAliases: () => [], unRegisterAlias: () => [], - getByGroup: (group: VisGroups) => { - return (_visTypes.filter((type) => { - return type.group === group; - }) as unknown) as VisType[]; - }, + getByGroup: (group: VisGroups) => _visTypes.filter((type) => type.group === group), }; const addBasePath = (url: string) => `testbasepath${url}`; const settingsGet = jest.fn(); diff --git a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx index 2a9641607d17a..b0be73e486d6c 100644 --- a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx @@ -21,7 +21,7 @@ import { import { SearchSelection } from './search_selection'; import { GroupSelection } from './group_selection'; import { AggBasedSelection } from './agg_based_selection'; -import type { TypesStart, VisType, VisTypeAlias } from '../vis_types'; +import type { TypesStart, BaseVisType, VisTypeAlias } from '../vis_types'; import { UsageCollectionSetup } from '../../../../plugins/usage_collection/public'; import { EmbeddableStateTransfer } from '../../../embeddable/public'; import { VISUALIZE_ENABLE_LABS_SETTING } from '../../common/constants'; @@ -46,7 +46,7 @@ interface TypeSelectionProps { interface TypeSelectionState { showSearchVisModal: boolean; showGroups: boolean; - visType?: VisType; + visType?: BaseVisType; } // TODO: redirect logic is specific to visualise & dashboard @@ -129,7 +129,7 @@ class NewVisModal extends React.Component { + private onVisTypeSelected = (visType: BaseVisType | VisTypeAlias) => { if (!('aliasPath' in visType) && visType.requiresSearch && visType.options.showIndexSelection) { this.setState({ showSearchVisModal: true, @@ -144,7 +144,11 @@ class NewVisModal extends React.Component void; - visType: VisType; + visType: BaseVisType; uiSettings: IUiSettingsClient; savedObjects: SavedObjectsStart; goBack: () => void; diff --git a/test/interpreter_functional/snapshots/baseline/combined_test3.json b/test/interpreter_functional/snapshots/baseline/combined_test3.json index 2aa601a8d3631..64b1052552c8f 100644 --- a/test/interpreter_functional/snapshots/baseline/combined_test3.json +++ b/test/interpreter_functional/snapshots/baseline/combined_test3.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/final_output_test.json b/test/interpreter_functional/snapshots/baseline/final_output_test.json index 2aa601a8d3631..64b1052552c8f 100644 --- a/test/interpreter_functional/snapshots/baseline/final_output_test.json +++ b/test/interpreter_functional/snapshots/baseline/final_output_test.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_all_data.json b/test/interpreter_functional/snapshots/baseline/metric_all_data.json index dd779800cd452..0e1e5a723373f 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_all_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_all_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_invalid_data.json b/test/interpreter_functional/snapshots/baseline/metric_invalid_data.json index 0a47cdb8ff74a..c7b4a0325dc91 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_invalid_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_invalid_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[],"meta":{},"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[],"meta":{},"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json b/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json index 992d667fdce9f..fc8622a818dec 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json b/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json index 031c9f9ea5504..95c011f9259b9 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json +++ b/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":1000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":true,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":1000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":true,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json b/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json index 8c6fde201c8f1..f4a8cd1f14e18 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/partial_test_2.json b/test/interpreter_functional/snapshots/baseline/partial_test_2.json index 2aa601a8d3631..64b1052552c8f 100644 --- a/test/interpreter_functional/snapshots/baseline/partial_test_2.json +++ b/test/interpreter_functional/snapshots/baseline/partial_test_2.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/step_output_test3.json b/test/interpreter_functional/snapshots/baseline/step_output_test3.json index 2aa601a8d3631..64b1052552c8f 100644 --- a/test/interpreter_functional/snapshots/baseline/step_output_test3.json +++ b/test/interpreter_functional/snapshots/baseline/step_output_test3.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/combined_test3.json b/test/interpreter_functional/snapshots/session/combined_test3.json index 2aa601a8d3631..64b1052552c8f 100644 --- a/test/interpreter_functional/snapshots/session/combined_test3.json +++ b/test/interpreter_functional/snapshots/session/combined_test3.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/final_output_test.json b/test/interpreter_functional/snapshots/session/final_output_test.json index 2aa601a8d3631..64b1052552c8f 100644 --- a/test/interpreter_functional/snapshots/session/final_output_test.json +++ b/test/interpreter_functional/snapshots/session/final_output_test.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_all_data.json b/test/interpreter_functional/snapshots/session/metric_all_data.json index dd779800cd452..0e1e5a723373f 100644 --- a/test/interpreter_functional/snapshots/session/metric_all_data.json +++ b/test/interpreter_functional/snapshots/session/metric_all_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_invalid_data.json b/test/interpreter_functional/snapshots/session/metric_invalid_data.json index 0a47cdb8ff74a..c7b4a0325dc91 100644 --- a/test/interpreter_functional/snapshots/session/metric_invalid_data.json +++ b/test/interpreter_functional/snapshots/session/metric_invalid_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[],"meta":{},"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[],"meta":{},"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json b/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json index 992d667fdce9f..fc8622a818dec 100644 --- a/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json +++ b/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_percentage_mode.json b/test/interpreter_functional/snapshots/session/metric_percentage_mode.json index 031c9f9ea5504..95c011f9259b9 100644 --- a/test/interpreter_functional/snapshots/session/metric_percentage_mode.json +++ b/test/interpreter_functional/snapshots/session/metric_percentage_mode.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":1000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":true,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":1000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":true,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_single_metric_data.json b/test/interpreter_functional/snapshots/session/metric_single_metric_data.json index 8c6fde201c8f1..f4a8cd1f14e18 100644 --- a/test/interpreter_functional/snapshots/session/metric_single_metric_data.json +++ b/test/interpreter_functional/snapshots/session/metric_single_metric_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/partial_test_2.json b/test/interpreter_functional/snapshots/session/partial_test_2.json index 2aa601a8d3631..64b1052552c8f 100644 --- a/test/interpreter_functional/snapshots/session/partial_test_2.json +++ b/test/interpreter_functional/snapshots/session/partial_test_2.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/step_output_test3.json b/test/interpreter_functional/snapshots/session/step_output_test3.json index 2aa601a8d3631..64b1052552c8f 100644 --- a/test/interpreter_functional/snapshots/session/step_output_test3.json +++ b/test/interpreter_functional/snapshots/session/step_output_test3.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/kibana.json b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/kibana.json index 622cbd80090ba..33c8f3238dc47 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/kibana.json +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/kibana.json @@ -3,6 +3,7 @@ "version": "0.0.1", "kibanaVersion": "kibana", "requiredPlugins": [ + "expressions", "visualizations" ], "server": false, diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/plugin.ts b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/plugin.ts index 7de3965d66d35..d4a7a32ba42dd 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/plugin.ts +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/plugin.ts @@ -7,25 +7,42 @@ */ import { CoreSetup, Plugin } from 'kibana/public'; -import { VisualizationsSetup } from '../../../../../src/plugins/visualizations/public'; +import { VisualizationsSetup } from 'src/plugins/visualizations/public'; +import { Plugin as ExpressionsPlugin } from '../../../../../src/plugins/expressions/public'; + import { SelfChangingEditor } from './self_changing_vis/self_changing_editor'; -import { SelfChangingComponent } from './self_changing_vis/self_changing_components'; +import { selfChangingVisFn, SelfChangingVisParams } from './self_changing_vis_fn'; +import { selfChangingVisRenderer } from './self_changing_vis_renderer'; +import { toExpressionAst } from './to_ast'; export interface SetupDependencies { + expressions: ReturnType; visualizations: VisualizationsSetup; } export class CustomVisualizationsPublicPlugin implements Plugin { - public setup(core: CoreSetup, setupDeps: SetupDependencies) { - setupDeps.visualizations.createReactVisualization({ + public setup(core: CoreSetup, { expressions, visualizations }: SetupDependencies) { + /** + * Register an expression function with type "render" for your visualization + */ + expressions.registerFunction(selfChangingVisFn); + + /** + * Register a renderer for your visualization + */ + expressions.registerRenderer(selfChangingVisRenderer); + + /** + * Create the visualization type with definition + */ + visualizations.createBaseVisualization({ name: 'self_changing_vis', title: 'Self Changing Vis', icon: 'controlsHorizontal', description: 'This visualization is able to change its own settings, that you could also set in the editor.', visConfig: { - component: SelfChangingComponent, defaults: { counter: 0, }, @@ -39,7 +56,7 @@ export class CustomVisualizationsPublicPlugin }, ], }, - requestHandler: 'none', + toExpressionAst, }); } diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_components.tsx b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_components.tsx index 3b3e57bff8aa1..7409bc48d3bc3 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_components.tsx +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_components.tsx @@ -7,14 +7,13 @@ */ import React, { useEffect } from 'react'; - import { EuiBadge } from '@elastic/eui'; +import { SelfChangingVisParams } from '../self_changing_vis_fn'; + interface SelfChangingComponentProps { - renderComplete: () => {}; - visParams: { - counter: number; - }; + renderComplete(): void; + visParams: SelfChangingVisParams; } export function SelfChangingComponent(props: SelfChangingComponentProps) { diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.tsx b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.tsx index ff3fd63e5d4aa..ad651ab55220d 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.tsx +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.tsx @@ -9,13 +9,13 @@ import React from 'react'; import { EuiFieldNumber, EuiFormRow } from '@elastic/eui'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public/vis_options_props'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; interface CounterParams { counter: number; } -export class SelfChangingEditor extends React.Component> { +export class SelfChangingEditor extends React.Component> { onCounterChange = (ev: any) => { this.props.setValue('counter', parseInt(ev.target.value, 10)); }; diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis_fn.ts b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis_fn.ts new file mode 100644 index 0000000000000..28bcc27f71ec9 --- /dev/null +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis_fn.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { ExpressionFunctionDefinition, Render } from 'src/plugins/expressions/public'; +import { KibanaContext } from 'src/plugins/data/public'; + +export interface SelfChangingVisParams { + counter: number; +} + +export interface SelfChangingVisRenderValue { + visParams: { + counter: number; + }; +} + +type Output = Promise>; + +export type SelfChangingVisExpressionFunctionDefinition = ExpressionFunctionDefinition< + 'self_changing_vis', + KibanaContext, + SelfChangingVisParams, + Output +>; + +export const selfChangingVisFn: SelfChangingVisExpressionFunctionDefinition = { + name: 'self_changing_vis', + type: 'render', + inputTypes: ['kibana_context'], + help: + 'The expression function definition should be registered for a custom visualization to be rendered', + args: { + counter: { + types: ['number'], + default: 0, + help: 'Visualization only argument with type number', + }, + }, + async fn(input, args) { + /** + * You can do any calculation you need before rendering. + * The function can also do asynchronous operations, e.x.: + * + const calculatedCounter = await new Promise((resolve) => + setTimeout(() => { + resolve(args.counter * 2); + }, 3000) + ); + */ + + return { + type: 'render', + as: 'self_changing_vis', + value: { + visParams: { + counter: args.counter, + }, + }, + }; + }, +}; diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis_renderer.tsx b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis_renderer.tsx new file mode 100644 index 0000000000000..ea24e6c294880 --- /dev/null +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis_renderer.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; + +import { ExpressionRenderDefinition } from 'src/plugins/expressions'; +import { SelfChangingComponent } from './self_changing_vis/self_changing_components'; +import { SelfChangingVisRenderValue } from './self_changing_vis_fn'; + +export const selfChangingVisRenderer: ExpressionRenderDefinition = { + name: 'self_changing_vis', + reuseDomNode: true, + render: (domNode, { visParams }, handlers) => { + handlers.onDestroy(() => { + unmountComponentAtNode(domNode); + }); + + render(, domNode); + }, +}; diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/to_ast.ts b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/to_ast.ts new file mode 100644 index 0000000000000..0f7ecb7954d15 --- /dev/null +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/to_ast.ts @@ -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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { VisToExpressionAst } from 'src/plugins/visualizations/public'; +import { + buildExpression, + buildExpressionFunction, +} from '../../../../../src/plugins/expressions/public'; +import { + SelfChangingVisExpressionFunctionDefinition, + SelfChangingVisParams, +} from './self_changing_vis_fn'; + +export const toExpressionAst: VisToExpressionAst = (vis) => { + const { counter } = vis.params; + + const selfChangingVis = buildExpressionFunction( + 'self_changing_vis', + { counter } + ); + + const ast = buildExpression([selfChangingVis]); + + return ast.toAst(); +}; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index ef2149c4931fa..9d8951b03f39a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4693,7 +4693,6 @@ "visualizations.function.visDimension.format.help": "フォーマット", "visualizations.function.visDimension.formatParams.help": "フォーマットパラメーター", "visualizations.function.visDimension.help": "visConfig ディメンションオブジェクトを生成します", - "visualizations.functions.visualization.help": "シンプルなビジュアライゼーションです", "visualizations.initializeWithoutIndexPatternErrorMessage": "インデックスパターンなしで集約を初期化しようとしています", "visualizations.newVisWizard.aggBasedGroupDescription": "クラシック Visualize ライブラリを使用して、アグリゲーションに基づいてグラフを作成します。", "visualizations.newVisWizard.aggBasedGroupTitle": "アグリゲーションに基づく", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 08d064ce8a05c..afa8c6d4be51a 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4698,7 +4698,6 @@ "visualizations.function.visDimension.format.help": "格式", "visualizations.function.visDimension.formatParams.help": "格式参数", "visualizations.function.visDimension.help": "生成 visConfig 维度对象", - "visualizations.functions.visualization.help": "简单可视化", "visualizations.initializeWithoutIndexPatternErrorMessage": "正在尝试在不使用索引模式的情况下初始化聚合", "visualizations.newVisWizard.aggBasedGroupDescription": "使用我们的经典可视化库,基于聚合创建图表。", "visualizations.newVisWizard.aggBasedGroupTitle": "基于聚合", From 1994c5bfd95c3a102e55e667ea75f28a851bc8d0 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Tue, 26 Jan 2021 09:49:04 +0100 Subject: [PATCH 62/62] [Core] remove unused "pageNavigation" setting (#89160) * remove unused advanced setting * remove docs --- docs/management/advanced-options.asciidoc | 5 ----- .../ui_settings/settings/navigation.test.ts | 14 ------------- .../server/ui_settings/settings/navigation.ts | 20 ------------------- .../server/collectors/management/schema.ts | 1 - src/plugins/telemetry/schema/oss_plugins.json | 3 --- .../translations/translations/ja-JP.json | 4 ---- .../translations/translations/zh-CN.json | 4 ---- 7 files changed, 51 deletions(-) diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index c2306b80734d8..bf4f7d9d82704 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -134,11 +134,6 @@ The maximum numbers of buckets that a single data source can return. This might arise when the user selects a short interval (for example, 1s) for a long time period (1 year). -[[pagenavigation]]`pageNavigation`:: -The style of navigation menu for Kibana. Choices are Legacy, the legacy style -where every plugin is represented in the nav, and Modern, a new format that -bundles related plugins together in flyaway nested navigation. - [[query-allowleadingwildcards]]`query:allowLeadingWildcards`:: Allows a wildcard (*) as the first character in a query clause. Only applies when experimental query features are enabled in the query bar. To disallow diff --git a/src/core/server/ui_settings/settings/navigation.test.ts b/src/core/server/ui_settings/settings/navigation.test.ts index 1ba81b4e79f46..0e6fbdfdbdb6e 100644 --- a/src/core/server/ui_settings/settings/navigation.test.ts +++ b/src/core/server/ui_settings/settings/navigation.test.ts @@ -28,18 +28,4 @@ describe('navigation settings', () => { ); }); }); - - describe('pageNavigation', () => { - const validate = getValidationFn(navigationSettings.pageNavigation); - - it('should only accept valid values', () => { - expect(() => validate('modern')).not.toThrow(); - expect(() => validate('legacy')).not.toThrow(); - expect(() => validate('invalid')).toThrowErrorMatchingInlineSnapshot(` -"types that failed validation: -- [0]: expected value to equal [modern] -- [1]: expected value to equal [legacy]" -`); - }); - }); }); diff --git a/src/core/server/ui_settings/settings/navigation.ts b/src/core/server/ui_settings/settings/navigation.ts index 38064db9e9388..937af4bb9aad1 100644 --- a/src/core/server/ui_settings/settings/navigation.ts +++ b/src/core/server/ui_settings/settings/navigation.ts @@ -37,25 +37,5 @@ export const getNavigationSettings = (): Record => { 'The route must be a relative URL.', }), }, - pageNavigation: { - name: i18n.translate('core.ui_settings.params.pageNavigationName', { - defaultMessage: 'Side nav style', - }), - value: 'modern', - description: i18n.translate('core.ui_settings.params.pageNavigationDesc', { - defaultMessage: 'Change the style of navigation', - }), - type: 'select', - options: ['modern', 'legacy'], - optionLabels: { - modern: i18n.translate('core.ui_settings.params.pageNavigationModern', { - defaultMessage: 'Modern', - }), - legacy: i18n.translate('core.ui_settings.params.pageNavigationLegacy', { - defaultMessage: 'Legacy', - }), - }, - schema: schema.oneOf([schema.literal('modern'), schema.literal('legacy')]), - }, }; }; diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index d75b2981035f4..28eeb461f7a86 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -77,7 +77,6 @@ export const stackManagementSchema: MakeSchemaFrom = { 'sort:options': { type: 'keyword' }, 'savedObjects:listingLimit': { type: 'long' }, 'query:queryString:options': { type: 'keyword' }, - pageNavigation: { type: 'keyword' }, 'metrics:max_buckets': { type: 'long' }, 'query:allowLeadingWildcards': { type: 'boolean' }, metaFields: { type: 'keyword' }, // it's an array diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 27d9b5ce83203..7bac6a809eca3 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -4268,9 +4268,6 @@ "query:queryString:options": { "type": "keyword" }, - "pageNavigation": { - "type": "keyword" - }, "metrics:max_buckets": { "type": "long" }, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 9d8951b03f39a..93267c950c10e 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -529,10 +529,6 @@ "core.ui_settings.params.notifications.infoLifetimeTitle": "情報通知時間", "core.ui_settings.params.notifications.warningLifetimeText": "警告通知が画面に表示される時間(ミリ秒単位)です。{infinityValue}に設定すると、無効になります。", "core.ui_settings.params.notifications.warningLifetimeTitle": "警告通知時間", - "core.ui_settings.params.pageNavigationDesc": "ナビゲーションのスタイルを変更", - "core.ui_settings.params.pageNavigationLegacy": "レガシー", - "core.ui_settings.params.pageNavigationModern": "モダン", - "core.ui_settings.params.pageNavigationName": "サイドナビゲーションスタイル", "core.ui_settings.params.storeUrlText": "URLが長くなりすぎるためブラウザーが対応できない場合があります。セッションストレージにURLの一部を保存することでこの問題に対処できるかどうかをテストしています。結果を教えてください!", "core.ui_settings.params.storeUrlTitle": "セッションストレージにURLを格納", "core.ui_settings.params.themeVersionText": "現在のバージョンと次のバージョンのKibanaで使用されるテーマを切り替えます。この設定を適用するにはページの更新が必要です。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index afa8c6d4be51a..41bdf97333dd7 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -529,10 +529,6 @@ "core.ui_settings.params.notifications.infoLifetimeTitle": "信息通知生存时间", "core.ui_settings.params.notifications.warningLifetimeText": "在屏幕上显示警告通知的时间(毫秒)。设置为 {infinityValue} 将禁用此项。", "core.ui_settings.params.notifications.warningLifetimeTitle": "警告通知生存时间", - "core.ui_settings.params.pageNavigationDesc": "更改导航样式", - "core.ui_settings.params.pageNavigationLegacy": "旧版", - "core.ui_settings.params.pageNavigationModern": "现代", - "core.ui_settings.params.pageNavigationName": "侧边导航样式", "core.ui_settings.params.storeUrlText": "有时,URL 可能会变得过长,使某些浏览器无法进行处理。为此,我们将正测试在会话存储中存储 URL 的组成部分是否会有所帮助。请向我们反馈您的体验!", "core.ui_settings.params.storeUrlTitle": "将 URL 存储在会话存储中", "core.ui_settings.params.themeVersionText": "在用于当前版和下一版 Kibana 的主题之间切换。需要刷新页面,才能应用设置。",