- - - {routes.map((route, i) => ( @@ -93,7 +85,9 @@ export function ApmAppRoot({ - + + + diff --git a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/add_environments.tsx b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/add_environments.tsx index db51d8fe40e55..6a11f862994e2 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/add_environments.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/add_environments.tsx @@ -26,6 +26,7 @@ import { useFetcher, FETCH_STATUS } from '../../../../hooks/use_fetcher'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { createJobs } from './create_jobs'; import { getEnvironmentLabel } from '../../../../../common/environment_filter_values'; +import { useAnomalyDetectionJobsContext } from '../../../../context/anomaly_detection_jobs/use_anomaly_detection_jobs_context'; interface Props { currentEnvironments: string[]; @@ -38,6 +39,7 @@ export function AddEnvironments({ onCancel, }: Props) { const { notifications, application } = useApmPluginContext().core; + const { anomalyDetectionJobsRefetch } = useAnomalyDetectionJobsContext(); const canCreateJob = !!application.capabilities.ml.canCreateJob; const { toasts } = notifications; const { data = [], status } = useFetcher( @@ -158,6 +160,7 @@ export function AddEnvironments({ toasts, }); if (success) { + anomalyDetectionJobsRefetch(); onCreateJobSuccess(); } setIsSaving(false); diff --git a/x-pack/plugins/apm/public/components/app/Settings/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/index.tsx index 61f68a74be9b7..36c36e3957e96 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/index.tsx @@ -16,6 +16,8 @@ import { import { i18n } from '@kbn/i18n'; import React, { ReactNode, useState } from 'react'; import { RouteComponentProps } from 'react-router-dom'; +import { HeaderMenuPortal } from '../../../../../observability/public'; +import { ActionMenu } from '../../../application/action_menu'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { getAPMHref } from '../../shared/Links/apm/APMLink'; import { HomeLink } from '../../shared/Links/apm/HomeLink'; @@ -25,7 +27,7 @@ interface SettingsProps extends RouteComponentProps<{}> { } export function Settings({ children, location }: SettingsProps) { - const { core } = useApmPluginContext(); + const { appMountParameters, core } = useApmPluginContext(); const { basePath } = core.http; const canAccessML = !!core.application.capabilities.ml?.canAccessML; const { search, pathname } = location; @@ -42,6 +44,11 @@ export function Settings({ children, location }: SettingsProps) { return ( <> + + + diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx index 1d67ff03b675d..9c4728488d96a 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx @@ -16,6 +16,7 @@ import { i18n } from '@kbn/i18n'; import React, { useEffect } from 'react'; import { toMountPoint } from '../../../../../../../src/plugins/kibana_react/public'; import { useTrackPageview } from '../../../../../observability/public'; +import { useAnomalyDetectionJobsContext } from '../../../context/anomaly_detection_jobs/use_anomaly_detection_jobs_context'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { useLocalStorage } from '../../../hooks/useLocalStorage'; @@ -25,7 +26,6 @@ import { SearchBar } from '../../shared/search_bar'; import { NoServicesMessage } from './no_services_message'; import { ServiceList } from './ServiceList'; import { MLCallout } from './ServiceList/MLCallout'; -import { useAnomalyDetectionJobsFetcher } from './use_anomaly_detection_jobs_fetcher'; const initialData = { items: [], @@ -108,7 +108,7 @@ export function ServiceInventory() { const { anomalyDetectionJobsData, anomalyDetectionJobsStatus, - } = useAnomalyDetectionJobsFetcher(); + } = useAnomalyDetectionJobsContext(); const [userHasDismissedCallout, setUserHasDismissedCallout] = useLocalStorage( 'apm.userHasDismissedServiceInventoryMlCallout', diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx index 419b66da5d222..e1e74e35a7bb2 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx @@ -24,7 +24,7 @@ import { clearCache } from '../../../services/rest/callApi'; import * as useDynamicIndexPatternHooks from '../../../hooks/use_dynamic_index_pattern'; import { SessionStorageMock } from '../../../services/__mocks__/SessionStorageMock'; import { MockUrlParamsContextProvider } from '../../../context/url_params_context/mock_url_params_context_provider'; -import * as hook from './use_anomaly_detection_jobs_fetcher'; +import * as hook from '../../../context/anomaly_detection_jobs/use_anomaly_detection_jobs_context'; import { TimeRangeComparisonType } from '../../shared/time_comparison/get_time_range_comparison'; const KibanaReactContext = createKibanaReactContext({ @@ -78,9 +78,10 @@ describe('ServiceInventory', () => { global.sessionStorage = new SessionStorageMock(); clearCache(); - jest.spyOn(hook, 'useAnomalyDetectionJobsFetcher').mockReturnValue({ - anomalyDetectionJobsStatus: FETCH_STATUS.SUCCESS, + jest.spyOn(hook, 'useAnomalyDetectionJobsContext').mockReturnValue({ anomalyDetectionJobsData: { jobs: [], hasLegacyJobs: false }, + anomalyDetectionJobsStatus: FETCH_STATUS.SUCCESS, + anomalyDetectionJobsRefetch: () => {}, }); jest diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/use_anomaly_detection_jobs_fetcher.ts b/x-pack/plugins/apm/public/components/app/service_inventory/use_anomaly_detection_jobs_fetcher.ts deleted file mode 100644 index 4d0d5902f8847..0000000000000 --- a/x-pack/plugins/apm/public/components/app/service_inventory/use_anomaly_detection_jobs_fetcher.ts +++ /dev/null @@ -1,21 +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 { useFetcher } from '../../../hooks/use_fetcher'; - -export function useAnomalyDetectionJobsFetcher() { - const { data, status } = useFetcher( - (callApmApi) => - callApmApi({ - endpoint: `GET /api/apm/settings/anomaly-detection/jobs`, - }), - [], - { showToastOnError: false } - ); - - return { anomalyDetectionJobsData: data, anomalyDetectionJobsStatus: status }; -} diff --git a/x-pack/plugins/apm/public/components/shared/ApmHeader/index.tsx b/x-pack/plugins/apm/public/components/shared/ApmHeader/index.tsx index f81157c5cffd5..f94bba84526a7 100644 --- a/x-pack/plugins/apm/public/components/shared/ApmHeader/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/ApmHeader/index.tsx @@ -8,6 +8,9 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React, { ReactNode } from 'react'; import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; +import { HeaderMenuPortal } from '../../../../../observability/public'; +import { ActionMenu } from '../../../application/action_menu'; +import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { EnvironmentFilter } from '../EnvironmentFilter'; const HeaderFlexGroup = euiStyled(EuiFlexGroup)` @@ -17,8 +20,13 @@ const HeaderFlexGroup = euiStyled(EuiFlexGroup)` `; export function ApmHeader({ children }: { children: ReactNode }) { + const { setHeaderActionMenu } = useApmPluginContext().appMountParameters; + return ( + + + {children} diff --git a/x-pack/plugins/apm/public/context/anomaly_detection_jobs/anomaly_detection_jobs_context.tsx b/x-pack/plugins/apm/public/context/anomaly_detection_jobs/anomaly_detection_jobs_context.tsx new file mode 100644 index 0000000000000..b8d6feda826a5 --- /dev/null +++ b/x-pack/plugins/apm/public/context/anomaly_detection_jobs/anomaly_detection_jobs_context.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { createContext, ReactChild, useState } from 'react'; +import { FETCH_STATUS, useFetcher } from '../../hooks/use_fetcher'; +import { APIReturnType } from '../../services/rest/createCallApmApi'; + +export interface AnomalyDetectionJobsContextValue { + anomalyDetectionJobsData?: APIReturnType<'GET /api/apm/settings/anomaly-detection/jobs'>; + anomalyDetectionJobsStatus: FETCH_STATUS; + anomalyDetectionJobsRefetch: () => void; +} + +export const AnomalyDetectionJobsContext = createContext( + {} as AnomalyDetectionJobsContextValue +); + +export function AnomalyDetectionJobsContextProvider({ + children, +}: { + children: ReactChild; +}) { + const [fetchId, setFetchId] = useState(0); + const refetch = () => setFetchId((id) => id + 1); + + const { data, status } = useFetcher( + (callApmApi) => + callApmApi({ + endpoint: `GET /api/apm/settings/anomaly-detection/jobs`, + }), + [fetchId], // eslint-disable-line react-hooks/exhaustive-deps + { showToastOnError: false } + ); + + return ( + + {children} + + ); +} diff --git a/x-pack/plugins/apm/public/context/anomaly_detection_jobs/use_anomaly_detection_jobs_context.ts b/x-pack/plugins/apm/public/context/anomaly_detection_jobs/use_anomaly_detection_jobs_context.ts new file mode 100644 index 0000000000000..4a81bd9ec9da7 --- /dev/null +++ b/x-pack/plugins/apm/public/context/anomaly_detection_jobs/use_anomaly_detection_jobs_context.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useContext } from 'react'; +import { AnomalyDetectionJobsContext } from './anomaly_detection_jobs_context'; + +export function useAnomalyDetectionJobsContext() { + return useContext(AnomalyDetectionJobsContext); +} From 1124fa2168e2619f917685b8612393a1134a59d2 Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Tue, 9 Mar 2021 16:08:21 -0500 Subject: [PATCH 14/56] Renamed cancel button to Switch to view mode. Disabled switch to view mode when on new dashboard (#94147) --- .../application/top_nav/get_top_nav_config.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/plugins/dashboard/public/application/top_nav/get_top_nav_config.ts b/src/plugins/dashboard/public/application/top_nav/get_top_nav_config.ts index b1b1386c00b19..da14f98468256 100644 --- a/src/plugins/dashboard/public/application/top_nav/get_top_nav_config.ts +++ b/src/plugins/dashboard/public/application/top_nav/get_top_nav_config.ts @@ -46,11 +46,18 @@ export function getTopNavConfig( const navItems: TopNavMenuData[] = [ getOptionsConfig(actions[TopNavIds.OPTIONS], disableButton), getShareConfig(actions[TopNavIds.SHARE], disableButton), - getViewConfig(actions[TopNavIds.EXIT_EDIT_MODE], disableButton), - getSaveConfig(actions[TopNavIds.SAVE], options.isNewDashboard, disableButton), ]; if (!options.isNewDashboard) { + navItems.push( + getSaveConfig(actions[TopNavIds.SAVE], options.isNewDashboard, disableButton) + ); + navItems.push(getViewConfig(actions[TopNavIds.EXIT_EDIT_MODE], disableButton)); navItems.push(getQuickSave(actions[TopNavIds.QUICK_SAVE], disableButton, options.isDirty)); + } else { + navItems.push(getViewConfig(actions[TopNavIds.EXIT_EDIT_MODE], true)); + navItems.push( + getSaveConfig(actions[TopNavIds.SAVE], options.isNewDashboard, disableButton) + ); } return navItems; default: @@ -151,7 +158,7 @@ function getViewConfig(action: NavAction, disableButton?: boolean) { disableButton, id: 'cancel', label: i18n.translate('dashboard.topNave.cancelButtonAriaLabel', { - defaultMessage: 'Cancel', + defaultMessage: 'Switch to view mode', }), description: i18n.translate('dashboard.topNave.viewConfigDescription', { defaultMessage: 'Switch to view-only mode', From ff2b3beaa84b1520b88bc43d10238250310ff4aa Mon Sep 17 00:00:00 2001 From: Rashmi Kulkarni Date: Tue, 9 Mar 2021 13:31:32 -0800 Subject: [PATCH 15/56] Unskipping Dashboard listing functional test (#93843) * fixes https://github.com/elastic/kibana/issues/74449 * unskipping to check the flakiness --- test/functional/apps/dashboard/dashboard_listing.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/functional/apps/dashboard/dashboard_listing.ts b/test/functional/apps/dashboard/dashboard_listing.ts index 86a3aac1f32c2..d73c6cf0b9899 100644 --- a/test/functional/apps/dashboard/dashboard_listing.ts +++ b/test/functional/apps/dashboard/dashboard_listing.ts @@ -16,7 +16,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const listingTable = getService('listingTable'); // FLAKY: https://github.com/elastic/kibana/issues/86948 - describe.skip('dashboard listing page', function describeIndexTests() { + describe('dashboard listing page', function describeIndexTests() { const dashboardName = 'Dashboard Listing Test'; before(async function () { @@ -126,7 +126,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(onDashboardLandingPage).to.equal(false); }); - it.skip('title match is case insensitive', async function () { + it('title match is case insensitive', async function () { await PageObjects.dashboard.gotoDashboardLandingPage(); const currentUrl = await browser.getCurrentUrl(); const newUrl = currentUrl + '&title=two%20words'; From 3992ed13dbbe4f86e6da290a60930baf2381f9df Mon Sep 17 00:00:00 2001 From: Marshall Main <55718608+marshallmain@users.noreply.github.com> Date: Tue, 9 Mar 2021 13:39:23 -0800 Subject: [PATCH 16/56] Move exceptions builder to lists plugin (#94002) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../build_exceptions_filter.test.ts | 156 +++++++++--------- .../exceptions}/build_exceptions_filter.ts | 25 +-- .../plugins/lists/common/exceptions/index.ts | 8 + .../plugins/lists/common/exceptions/types.ts | 24 +++ .../plugins/lists/common/exceptions/utils.ts | 24 +++ x-pack/plugins/lists/common/shared_exports.ts | 2 + .../detection_engine/get_query_filter.ts | 2 +- .../common/detection_engine/types.ts | 18 -- .../common/shared_imports.ts | 1 + .../server/lib/machine_learning/index.ts | 2 +- 10 files changed, 149 insertions(+), 113 deletions(-) rename x-pack/plugins/{security_solution/common/detection_engine => lists/common/exceptions}/build_exceptions_filter.test.ts (97%) rename x-pack/plugins/{security_solution/common/detection_engine => lists/common/exceptions}/build_exceptions_filter.ts (99%) create mode 100644 x-pack/plugins/lists/common/exceptions/index.ts create mode 100644 x-pack/plugins/lists/common/exceptions/types.ts create mode 100644 x-pack/plugins/lists/common/exceptions/utils.ts diff --git a/x-pack/plugins/security_solution/common/detection_engine/build_exceptions_filter.test.ts b/x-pack/plugins/lists/common/exceptions/build_exceptions_filter.test.ts similarity index 97% rename from x-pack/plugins/security_solution/common/detection_engine/build_exceptions_filter.test.ts rename to x-pack/plugins/lists/common/exceptions/build_exceptions_filter.test.ts index 689c60e67f14f..291831777e471 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/build_exceptions_filter.test.ts +++ b/x-pack/plugins/lists/common/exceptions/build_exceptions_filter.test.ts @@ -5,41 +5,35 @@ * 2.0. */ +import { getEntryMatchExcludeMock, getEntryMatchMock } from '../schemas/types/entry_match.mock'; import { - getEntryMatchMock, - getEntryMatchExcludeMock, -} from '../../../lists/common/schemas/types/entry_match.mock'; -import { - getEntryMatchAnyMock, getEntryMatchAnyExcludeMock, -} from '../../../lists/common/schemas/types/entry_match_any.mock'; -import { - getEntryExistsMock, - getEntryExistsExcludedMock, -} from '../../../lists/common/schemas/types/entry_exists.mock'; + getEntryMatchAnyMock, +} from '../schemas/types/entry_match_any.mock'; +import { getEntryExistsExcludedMock, getEntryExistsMock } from '../schemas/types/entry_exists.mock'; import { - getEntryNestedMock, getEntryNestedExcludeMock, getEntryNestedMixedEntries, -} from '../../../lists/common/schemas/types/entry_nested.mock'; + getEntryNestedMock, +} from '../schemas/types/entry_nested.mock'; import { getExceptionListItemSchemaMock, getExceptionListItemSchemaXMock, -} from '../../../lists/common/schemas/response/exception_list_item_schema.mock'; +} from '../schemas/response/exception_list_item_schema.mock'; +import { EntryMatchAny, ExceptionListItemSchema } from '../schemas'; import { + ExceptionItemSansLargeValueLists, + buildExceptionFilter, buildExceptionItemFilter, buildExclusionClause, buildExistsClause, buildMatchAnyClause, buildMatchClause, buildNestedClause, - createOrClauses, chunkExceptions, - buildExceptionFilter, - ExceptionItemSansLargeValueLists, + createOrClauses, } from './build_exceptions_filter'; -import { EntryMatchAny, ExceptionListItemSchema } from '../../common/shared_imports'; import { hasLargeValueList } from './utils'; const modifiedGetEntryMatchAnyMock = (): EntryMatchAny => ({ @@ -59,22 +53,22 @@ describe('build_exceptions_filter', () => { describe('buildExceptionFilter', () => { test('it should return undefined if no exception items', () => { const booleanFilter = buildExceptionFilter({ - lists: [], - excludeExceptions: false, chunkSize: 1, + excludeExceptions: false, + lists: [], }); expect(booleanFilter).toBeUndefined(); }); test('it should build a filter given an exception list', () => { const booleanFilter = buildExceptionFilter({ - lists: [getExceptionListItemSchemaMock()], - excludeExceptions: false, chunkSize: 1, + excludeExceptions: false, + lists: [getExceptionListItemSchemaMock()], }); expect(booleanFilter).toEqual({ - meta: { alias: null, negate: false, disabled: false }, + meta: { alias: null, disabled: false, negate: false }, query: { bool: { should: [ @@ -86,10 +80,10 @@ describe('build_exceptions_filter', () => { path: 'some.parentField', query: { bool: { + minimum_should_match: 1, should: [ { match_phrase: { 'some.parentField.nested.field': 'some value' } }, ], - minimum_should_match: 1, }, }, score_mode: 'none', @@ -97,8 +91,8 @@ describe('build_exceptions_filter', () => { }, { bool: { - should: [{ match_phrase: { 'some.not.nested.field': 'some value' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'some.not.nested.field': 'some value' } }], }, }, ], @@ -123,15 +117,15 @@ describe('build_exceptions_filter', () => { entries: [{ field: 'user.name', operator: 'included', type: 'match', value: 'name' }], }; const exceptionFilter = buildExceptionFilter({ - lists: [exceptionItem1, exceptionItem2], - excludeExceptions: true, chunkSize: 2, + excludeExceptions: true, + lists: [exceptionItem1, exceptionItem2], }); expect(exceptionFilter).toEqual({ meta: { alias: null, - negate: true, disabled: false, + negate: true, }, query: { bool: { @@ -201,16 +195,16 @@ describe('build_exceptions_filter', () => { entries: [{ field: 'file.path', operator: 'included', type: 'match', value: '/safe/path' }], }; const exceptionFilter = buildExceptionFilter({ - lists: [exceptionItem1, exceptionItem2, exceptionItem3], - excludeExceptions: true, chunkSize: 2, + excludeExceptions: true, + lists: [exceptionItem1, exceptionItem2, exceptionItem3], }); expect(exceptionFilter).toEqual({ meta: { alias: null, - negate: true, disabled: false, + negate: true, }, query: { bool: { @@ -298,13 +292,13 @@ describe('build_exceptions_filter', () => { ]; const booleanFilter = buildExceptionFilter({ - lists: exceptions, - excludeExceptions: true, chunkSize: 1, + excludeExceptions: true, + lists: exceptions, }); expect(booleanFilter).toEqual({ - meta: { alias: null, negate: true, disabled: false }, + meta: { alias: null, disabled: false, negate: true }, query: { bool: { should: [ @@ -319,21 +313,23 @@ describe('build_exceptions_filter', () => { filter: [ { bool: { + minimum_should_match: 1, should: [ { match_phrase: { 'parent.field.host.name': 'some host name' }, }, ], - minimum_should_match: 1, }, }, { bool: { must_not: { bool: { + minimum_should_match: 1, should: [ { bool: { + minimum_should_match: 1, should: [ { match_phrase: { @@ -341,11 +337,11 @@ describe('build_exceptions_filter', () => { }, }, ], - minimum_should_match: 1, }, }, { bool: { + minimum_should_match: 1, should: [ { match_phrase: { @@ -353,19 +349,17 @@ describe('build_exceptions_filter', () => { }, }, ], - minimum_should_match: 1, }, }, ], - minimum_should_match: 1, }, }, }, }, { bool: { - should: [{ exists: { field: 'parent.field.host.name' } }], minimum_should_match: 1, + should: [{ exists: { field: 'parent.field.host.name' } }], }, }, ], @@ -382,21 +376,21 @@ describe('build_exceptions_filter', () => { should: [ { bool: { + minimum_should_match: 1, should: [ { bool: { - should: [{ match_phrase: { 'host.name': 'some "host" name' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'host.name': 'some "host" name' } }], }, }, { bool: { - should: [{ match_phrase: { 'host.name': 'some other host name' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'host.name': 'some other host name' } }], }, }, ], - minimum_should_match: 1, }, }, ], @@ -412,16 +406,16 @@ describe('build_exceptions_filter', () => { bool: { must_not: { bool: { - should: [{ exists: { field: 'host.name' } }], minimum_should_match: 1, + should: [{ exists: { field: 'host.name' } }], }, }, }, }, { bool: { - should: [{ match_phrase: { 'host.name': 'some host name' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'host.name': 'some host name' } }], }, }, ], @@ -520,18 +514,18 @@ describe('build_exceptions_filter', () => { filter: [ { bool: { + minimum_should_match: 1, should: [ { match_phrase: { 'parent.field.host.name': 'some host name' } }, ], - minimum_should_match: 1, }, }, { bool: { + minimum_should_match: 1, should: [ { match_phrase: { 'parent.field.host.name': 'some host name' } }, ], - minimum_should_match: 1, }, }, ], @@ -542,8 +536,8 @@ describe('build_exceptions_filter', () => { }, { bool: { - should: [{ match_phrase: { 'host.name': 'some host name' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'host.name': 'some host name' } }], }, }, ], @@ -560,19 +554,21 @@ describe('build_exceptions_filter', () => { filter: [ { bool: { + minimum_should_match: 1, should: [ { match_phrase: { 'parent.field.host.name': 'some host name' } }, ], - minimum_should_match: 1, }, }, { bool: { must_not: { bool: { + minimum_should_match: 1, should: [ { bool: { + minimum_should_match: 1, should: [ { match_phrase: { @@ -580,11 +576,11 @@ describe('build_exceptions_filter', () => { }, }, ], - minimum_should_match: 1, }, }, { bool: { + minimum_should_match: 1, should: [ { match_phrase: { @@ -592,19 +588,17 @@ describe('build_exceptions_filter', () => { }, }, ], - minimum_should_match: 1, }, }, ], - minimum_should_match: 1, }, }, }, }, { bool: { - should: [{ exists: { field: 'parent.field.host.name' } }], minimum_should_match: 1, + should: [{ exists: { field: 'parent.field.host.name' } }], }, }, ], @@ -615,29 +609,29 @@ describe('build_exceptions_filter', () => { }, { bool: { + minimum_should_match: 1, should: [ { bool: { - should: [{ match_phrase: { 'host.name': 'some "host" name' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'host.name': 'some "host" name' } }], }, }, { bool: { - should: [{ match_phrase: { 'host.name': 'some other host name' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'host.name': 'some other host name' } }], }, }, ], - minimum_should_match: 1, }, }, { bool: { must_not: { bool: { - should: [{ match_phrase: { 'host.name': 'some host name' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'host.name': 'some host name' } }], }, }, }, @@ -645,7 +639,7 @@ describe('build_exceptions_filter', () => { { bool: { must_not: { - bool: { should: [{ exists: { field: 'host.name' } }], minimum_should_match: 1 }, + bool: { minimum_should_match: 1, should: [{ exists: { field: 'host.name' } }] }, }, }, }, @@ -655,7 +649,7 @@ describe('build_exceptions_filter', () => { { bool: { must_not: { - bool: { should: [{ exists: { field: 'host.name' } }], minimum_should_match: 1 }, + bool: { minimum_should_match: 1, should: [{ exists: { field: 'host.name' } }] }, }, }, }, @@ -686,6 +680,7 @@ describe('build_exceptions_filter', () => { filter: [ { bool: { + minimum_should_match: 1, should: [ { match_phrase: { @@ -693,16 +688,17 @@ describe('build_exceptions_filter', () => { }, }, ], - minimum_should_match: 1, }, }, { bool: { must_not: { bool: { + minimum_should_match: 1, should: [ { bool: { + minimum_should_match: 1, should: [ { match_phrase: { @@ -710,11 +706,11 @@ describe('build_exceptions_filter', () => { }, }, ], - minimum_should_match: 1, }, }, { bool: { + minimum_should_match: 1, should: [ { match_phrase: { @@ -722,19 +718,17 @@ describe('build_exceptions_filter', () => { }, }, ], - minimum_should_match: 1, }, }, ], - minimum_should_match: 1, }, }, }, }, { bool: { - should: [{ exists: { field: 'parent.field.host.name' } }], minimum_should_match: 1, + should: [{ exists: { field: 'parent.field.host.name' } }], }, }, ], @@ -745,29 +739,29 @@ describe('build_exceptions_filter', () => { }, { bool: { + minimum_should_match: 1, should: [ { bool: { - should: [{ match_phrase: { 'host.name': 'some "host" name' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'host.name': 'some "host" name' } }], }, }, { bool: { - should: [{ match_phrase: { 'host.name': 'some other host name' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'host.name': 'some other host name' } }], }, }, ], - minimum_should_match: 1, }, }, { bool: { must_not: { bool: { - should: [{ match_phrase: { 'host.name': 'some host name' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'host.name': 'some host name' } }], }, }, }, @@ -775,7 +769,7 @@ describe('build_exceptions_filter', () => { { bool: { must_not: { - bool: { should: [{ exists: { field: 'host.name' } }], minimum_should_match: 1 }, + bool: { minimum_should_match: 1, should: [{ exists: { field: 'host.name' } }] }, }, }, }, @@ -905,21 +899,21 @@ describe('build_exceptions_filter', () => { bool: { must_not: { bool: { + minimum_should_match: 1, should: [ { bool: { - should: [{ match_phrase: { 'host.name': 'some host name' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'host.name': 'some host name' } }], }, }, { bool: { - should: [{ match_phrase: { 'host.name': 'some other host name' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'host.name': 'some other host name' } }], }, }, ], - minimum_should_match: 1, }, }, }, @@ -961,14 +955,14 @@ describe('build_exceptions_filter', () => { filter: [ { bool: { - should: [{ match_phrase: { 'parent.field.host.name': 'some host name' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'parent.field.host.name': 'some host name' } }], }, }, { bool: { - should: [{ match_phrase: { 'parent.field.host.name': 'some host name' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'parent.field.host.name': 'some host name' } }], }, }, ], @@ -992,8 +986,8 @@ describe('build_exceptions_filter', () => { bool: { must_not: { bool: { - should: [{ match_phrase: { 'parent.field.host.name': 'some host name' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'parent.field.host.name': 'some host name' } }], }, }, }, @@ -1002,17 +996,19 @@ describe('build_exceptions_filter', () => { bool: { must_not: { bool: { + minimum_should_match: 1, should: [ { bool: { + minimum_should_match: 1, should: [ { match_phrase: { 'parent.field.host.name': 'some host name' } }, ], - minimum_should_match: 1, }, }, { bool: { + minimum_should_match: 1, should: [ { match_phrase: { @@ -1020,11 +1016,9 @@ describe('build_exceptions_filter', () => { }, }, ], - minimum_should_match: 1, }, }, ], - minimum_should_match: 1, }, }, }, @@ -1048,25 +1042,27 @@ describe('build_exceptions_filter', () => { filter: [ { bool: { - should: [{ match_phrase: { 'parent.field.host.name': 'some host name' } }], minimum_should_match: 1, + should: [{ match_phrase: { 'parent.field.host.name': 'some host name' } }], }, }, { bool: { must_not: { bool: { + minimum_should_match: 1, should: [ { bool: { + minimum_should_match: 1, should: [ { match_phrase: { 'parent.field.host.name': 'some host name' } }, ], - minimum_should_match: 1, }, }, { bool: { + minimum_should_match: 1, should: [ { match_phrase: { @@ -1074,19 +1070,17 @@ describe('build_exceptions_filter', () => { }, }, ], - minimum_should_match: 1, }, }, ], - minimum_should_match: 1, }, }, }, }, { bool: { - should: [{ exists: { field: 'parent.field.host.name' } }], minimum_should_match: 1, + should: [{ exists: { field: 'parent.field.host.name' } }], }, }, ], diff --git a/x-pack/plugins/security_solution/common/detection_engine/build_exceptions_filter.ts b/x-pack/plugins/lists/common/exceptions/build_exceptions_filter.ts similarity index 99% rename from x-pack/plugins/security_solution/common/detection_engine/build_exceptions_filter.ts rename to x-pack/plugins/lists/common/exceptions/build_exceptions_filter.ts index e625254e49b23..0a9753b02a612 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/build_exceptions_filter.ts +++ b/x-pack/plugins/lists/common/exceptions/build_exceptions_filter.ts @@ -9,17 +9,18 @@ import { chunk } from 'lodash/fp'; import { Filter } from '../../../../../src/plugins/data/common'; import { - ExceptionListItemSchema, CreateExceptionListItemSchema, + EntryExists, EntryMatch, EntryMatchAny, EntryNested, + ExceptionListItemSchema, + entriesExists, entriesMatch, entriesMatchAny, - entriesExists, entriesNested, - EntryExists, -} from '../../../lists/common'; +} from '../schemas'; + import { BooleanFilter, NestedFilter } from './types'; import { hasLargeValueList } from './utils'; @@ -83,8 +84,8 @@ export const buildExceptionFilter = ({ const exceptionFilter: Filter = { meta: { alias: null, - negate: excludeExceptions, disabled: false, + negate: excludeExceptions, }, query: { bool: { @@ -108,8 +109,8 @@ export const buildExceptionFilter = ({ return { meta: { alias: null, - negate: false, disabled: false, + negate: false, }, query: { bool: { @@ -124,8 +125,8 @@ export const buildExceptionFilter = ({ return { meta: { alias: null, - negate: excludeExceptions, disabled: false, + negate: excludeExceptions, }, query: { bool: { @@ -148,6 +149,7 @@ export const buildMatchClause = (entry: EntryMatch): BooleanFilter => { const { field, operator, value } = entry; const matchClause = { bool: { + minimum_should_match: 1, should: [ { match_phrase: { @@ -155,7 +157,6 @@ export const buildMatchClause = (entry: EntryMatch): BooleanFilter => { }, }, ], - minimum_should_match: 1, }, }; @@ -172,6 +173,7 @@ export const getBaseMatchAnyClause = (entry: EntryMatchAny): BooleanFilter => { if (value.length === 1) { return { bool: { + minimum_should_match: 1, should: [ { match_phrase: { @@ -179,16 +181,17 @@ export const getBaseMatchAnyClause = (entry: EntryMatchAny): BooleanFilter => { }, }, ], - minimum_should_match: 1, }, }; } return { bool: { + minimum_should_match: 1, should: value.map((val) => { return { bool: { + minimum_should_match: 1, should: [ { match_phrase: { @@ -196,11 +199,9 @@ export const getBaseMatchAnyClause = (entry: EntryMatchAny): BooleanFilter => { }, }, ], - minimum_should_match: 1, }, }; }), - minimum_should_match: 1, }, }; }; @@ -220,6 +221,7 @@ export const buildExistsClause = (entry: EntryExists): BooleanFilter => { const { field, operator } = entry; const existsClause = { bool: { + minimum_should_match: 1, should: [ { exists: { @@ -227,7 +229,6 @@ export const buildExistsClause = (entry: EntryExists): BooleanFilter => { }, }, ], - minimum_should_match: 1, }, }; diff --git a/x-pack/plugins/lists/common/exceptions/index.ts b/x-pack/plugins/lists/common/exceptions/index.ts new file mode 100644 index 0000000000000..d9a9c47348e1a --- /dev/null +++ b/x-pack/plugins/lists/common/exceptions/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './build_exceptions_filter'; diff --git a/x-pack/plugins/lists/common/exceptions/types.ts b/x-pack/plugins/lists/common/exceptions/types.ts new file mode 100644 index 0000000000000..f4e0dffab3f25 --- /dev/null +++ b/x-pack/plugins/lists/common/exceptions/types.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface BooleanFilter { + bool: { + must?: unknown | unknown[]; + must_not?: unknown | unknown[]; + should?: unknown[]; + filter?: unknown | unknown[]; + minimum_should_match?: number; + }; +} + +export interface NestedFilter { + nested: { + path: string; + query: unknown | unknown[]; + score_mode: string; + }; +} diff --git a/x-pack/plugins/lists/common/exceptions/utils.ts b/x-pack/plugins/lists/common/exceptions/utils.ts new file mode 100644 index 0000000000000..d7dc706882cd5 --- /dev/null +++ b/x-pack/plugins/lists/common/exceptions/utils.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CreateExceptionListItemSchema, EntriesArray, ExceptionListItemSchema } from '../schemas'; + +export const hasLargeValueItem = ( + exceptionItems: Array +): boolean => { + return exceptionItems.some((exceptionItem) => hasLargeValueList(exceptionItem.entries)); +}; + +export const hasLargeValueList = (entries: EntriesArray): boolean => { + const found = entries.filter(({ type }) => type === 'list'); + return found.length > 0; +}; + +export const hasNestedEntry = (entries: EntriesArray): boolean => { + const found = entries.filter(({ type }) => type === 'nested'); + return found.length > 0; +}; diff --git a/x-pack/plugins/lists/common/shared_exports.ts b/x-pack/plugins/lists/common/shared_exports.ts index 4c4ee19d29bcd..06e72b0070dfe 100644 --- a/x-pack/plugins/lists/common/shared_exports.ts +++ b/x-pack/plugins/lists/common/shared_exports.ts @@ -48,4 +48,6 @@ export { OsTypeArray, } from './schemas'; +export { buildExceptionFilter } from './exceptions'; + export { ENDPOINT_LIST_ID, ENDPOINT_TRUSTED_APPS_LIST_ID } from './constants'; diff --git a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts index 7dfff1d5de081..8234c3a9a599d 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts @@ -16,7 +16,7 @@ import { CreateExceptionListItemSchema, } from '../../../lists/common/schemas'; import { ESBoolQuery } from '../typed_json'; -import { buildExceptionFilter } from './build_exceptions_filter'; +import { buildExceptionFilter } from '../shared_imports'; import { Query, Language, Index, TimestampOverrideOrUndefined } from './schemas/common/schemas'; export const getQueryFilter = ( diff --git a/x-pack/plugins/security_solution/common/detection_engine/types.ts b/x-pack/plugins/security_solution/common/detection_engine/types.ts index 0c390353e4e07..c0e502312b2ff 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/types.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/types.ts @@ -56,21 +56,3 @@ export interface EqlSearchResponse { events?: Array>; }; } - -export interface BooleanFilter { - bool: { - must?: unknown | unknown[]; - must_not?: unknown | unknown[]; - should?: unknown[]; - filter?: unknown | unknown[]; - minimum_should_match?: number; - }; -} - -export interface NestedFilter { - nested: { - path: string; - query: unknown | unknown[]; - score_mode: string; - }; -} diff --git a/x-pack/plugins/security_solution/common/shared_imports.ts b/x-pack/plugins/security_solution/common/shared_imports.ts index 94afbb948bf42..a578fb932068d 100644 --- a/x-pack/plugins/security_solution/common/shared_imports.ts +++ b/x-pack/plugins/security_solution/common/shared_imports.ts @@ -47,4 +47,5 @@ export { ENDPOINT_TRUSTED_APPS_LIST_ID, osTypeArray, OsTypeArray, + buildExceptionFilter, } from '../../lists/common'; diff --git a/x-pack/plugins/security_solution/server/lib/machine_learning/index.ts b/x-pack/plugins/security_solution/server/lib/machine_learning/index.ts index c3fea9f6d916f..6aac390a3f842 100644 --- a/x-pack/plugins/security_solution/server/lib/machine_learning/index.ts +++ b/x-pack/plugins/security_solution/server/lib/machine_learning/index.ts @@ -7,7 +7,7 @@ import { RequestParams } from '@elastic/elasticsearch'; -import { buildExceptionFilter } from '../../../common/detection_engine/build_exceptions_filter'; +import { buildExceptionFilter } from '../../../common/shared_imports'; import { ExceptionListItemSchema } from '../../../../lists/common'; import { AnomalyRecordDoc as Anomaly } from '../../../../ml/server'; import { SearchResponse } from '../types'; From 5d119cfcbb7b1a908a52fa5497ac21f4fbe5e971 Mon Sep 17 00:00:00 2001 From: Zacqary Adam Xeper Date: Tue, 9 Mar 2021 16:10:56 -0600 Subject: [PATCH 17/56] [Fleet] Return empty agents list when submitting a kuery with no keys (#93844) --- .../fleet/server/services/agents/crud_so.ts | 57 ++++++++++++------- .../fleet_api_integration/apis/agents/list.ts | 8 +++ 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/agents/crud_so.ts b/x-pack/plugins/fleet/server/services/agents/crud_so.ts index 028210b620a13..92ac7c9f6c08e 100644 --- a/x-pack/plugins/fleet/server/services/agents/crud_so.ts +++ b/x-pack/plugins/fleet/server/services/agents/crud_so.ts @@ -76,29 +76,42 @@ export async function listAgents( if (showInactive === false) { filters.push(ACTIVE_AGENT_CONDITION); } - let { saved_objects: agentSOs, total } = await soClient.find({ - type: AGENT_SAVED_OBJECT_TYPE, - filter: _joinFilters(filters) || '', - sortField, - sortOrder, - page, - perPage, - }); - // filtering for a range on the version string will not work, - // nor does filtering on a flattened field (local_metadata), so filter here - if (showUpgradeable) { - agentSOs = agentSOs.filter((agent) => - isAgentUpgradeable(savedObjectToAgent(agent), appContextService.getKibanaVersion()) - ); - total = agentSOs.length; + try { + let { saved_objects: agentSOs, total } = await soClient.find({ + type: AGENT_SAVED_OBJECT_TYPE, + filter: _joinFilters(filters) || '', + sortField, + sortOrder, + page, + perPage, + }); + // filtering for a range on the version string will not work, + // nor does filtering on a flattened field (local_metadata), so filter here + if (showUpgradeable) { + agentSOs = agentSOs.filter((agent) => + isAgentUpgradeable(savedObjectToAgent(agent), appContextService.getKibanaVersion()) + ); + total = agentSOs.length; + } + + return { + agents: agentSOs.map(savedObjectToAgent), + total, + page, + perPage, + }; + } catch (e) { + if (e.output?.payload?.message?.startsWith('The key is empty')) { + return { + agents: [], + total: 0, + page: 0, + perPage: 0, + }; + } else { + throw e; + } } - - return { - agents: agentSOs.map(savedObjectToAgent), - total, - page, - perPage, - }; } export async function listAllAgents( diff --git a/x-pack/test/fleet_api_integration/apis/agents/list.ts b/x-pack/test/fleet_api_integration/apis/agents/list.ts index 5fcf43f274b31..7fa88be708077 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/list.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/list.ts @@ -102,6 +102,14 @@ export default function ({ getService }: FtrProviderContext) { it('should return a 400 when given an invalid "kuery" value', async () => { await supertest.get(`/api/fleet/agents?kuery=.test%3A`).expect(400); }); + + it('should return a 200 and an empty list when given a "kuery" value with a missing saved object type', async () => { + const { body: apiResponse } = await supertest + .get(`/api/fleet/agents?kuery=m`) // missing saved object type + .expect(200); + expect(apiResponse.total).to.eql(0); + }); + it('should accept a valid "kuery" value', async () => { const filter = encodeURIComponent('fleet-agents.access_api_key_id : "api-key-2"'); const { body: apiResponse } = await supertest From 086762da6a2be8556e8f07b3407bf99b8a77f2c6 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Tue, 9 Mar 2021 14:39:46 -0800 Subject: [PATCH 18/56] [Actions] Replaces legacy es client with the ElasticsearchClient (#93361) * [Actions] Replaces legasy es client with the ElasticsearchClient * fixed build * fixed tests --- x-pack/plugins/actions/README.md | 5 +- .../actions/server/actions_client.test.ts | 88 +++++---- .../plugins/actions/server/actions_client.ts | 10 +- .../builtin_action_types/es_index.test.ts | 100 ++++++---- .../server/builtin_action_types/es_index.ts | 2 +- .../server/lib/task_runner_factory.test.ts | 1 - x-pack/plugins/actions/server/mocks.ts | 2 - x-pack/plugins/actions/server/plugin.test.ts | 4 +- x-pack/plugins/actions/server/plugin.ts | 9 +- x-pack/plugins/actions/server/types.ts | 7 - .../server/usage/actions_telemetry.test.ts | 181 +++++++++--------- .../actions/server/usage/actions_telemetry.ts | 12 +- x-pack/plugins/actions/server/usage/task.ts | 17 +- .../plugins/alerts/server/action_types.ts | 15 +- .../tests/actions/execute.ts | 2 - .../tests/alerting/alerts.ts | 2 - 16 files changed, 239 insertions(+), 218 deletions(-) diff --git a/x-pack/plugins/actions/README.md b/x-pack/plugins/actions/README.md index 3ec8545017ca1..5b4a197eea462 100644 --- a/x-pack/plugins/actions/README.md +++ b/x-pack/plugins/actions/README.md @@ -137,10 +137,9 @@ This is the primary function for an action type. Whenever the action needs to ex | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | actionId | The action saved object id that the action type is executing for. | | config | The action configuration. If you would like to validate the config before being passed to the executor, define `validate.config` within the action type. | -| secrets | The decrypted secrets object given to an action. This comes from the action saved object that is partially or fully encrypted within the data store. If you would like to validate the secrets object before being passed to the executor, define `validate.secrets` within the action type. | +| secrets | The decrypted secrets object given to an action. This comes from the action saved object that is partially or fully encrypted within the data store. If you would like to validate the secrets object before being passed to the executor, define `validate.secrets` within the action type. | | params | Parameters for the execution. These will be given at execution time by either an alert or manually provided when calling the plugin provided execute function. | -| services.callCluster(path, opts) | Use this to do Elasticsearch queries on the cluster Kibana connects to. This function is the same as any other `callCluster` in Kibana but runs in the context of the user who is calling the action when security is enabled. | -| services.getLegacyScopedClusterClient | This function returns an instance of the LegacyScopedClusterClient scoped to the user who is calling the action when security is enabled. | +| services.scopedClusterClient | Use this to do Elasticsearch queries on the cluster Kibana connects to. Serves the same purpose as the normal IClusterClient, but exposes an additional `asCurrentUser` method that doesn't use credentials of the Kibana internal user (as `asInternalUser` does) to request Elasticsearch API, but rather passes HTTP headers extracted from the current user request to the API instead.| | services.savedObjectsClient | This is an instance of the saved objects client. This provides the ability to do CRUD on any saved objects within the same space the alert lives in.

The scope of the saved objects client is tied to the user in context calling the execute API or the API key provided to the execute plugin function (only when security isenabled). | | services.log(tags, [data], [timestamp]) | Use this to create server logs. (This is the same function as server.log) diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index 3bd8bb5f1ba52..98b9b46fac48d 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -27,6 +27,8 @@ import { actionExecutorMock } from './lib/action_executor.mock'; import uuid from 'uuid'; import { ActionsAuthorization } from './authorization/actions_authorization'; import { actionsAuthorizationMock } from './authorization/actions_authorization.mock'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { elasticsearchClientMock } from '../../../../src/core/server/elasticsearch/client/mocks'; jest.mock('../../../../src/core/server/saved_objects/service/lib/utils', () => ({ SavedObjectsUtils: { @@ -36,7 +38,7 @@ jest.mock('../../../../src/core/server/saved_objects/service/lib/utils', () => ( const defaultKibanaIndex = '.kibana'; const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); -const scopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); +const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); const actionExecutor = actionExecutorMock.create(); const authorization = actionsAuthorizationMock.create(); const executionEnqueuer = jest.fn(); @@ -741,12 +743,14 @@ describe('getAll()', () => { ], }; unsecuredSavedObjectsClient.find.mockResolvedValueOnce(expectedResult); - scopedClusterClient.callAsInternalUser.mockResolvedValueOnce({ - aggregations: { - '1': { doc_count: 6 }, - testPreconfigured: { doc_count: 2 }, - }, - }); + scopedClusterClient.asInternalUser.search.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + aggregations: { + '1': { doc_count: 6 }, + testPreconfigured: { doc_count: 2 }, + }, + }) + ); actionsClient = new ActionsClient({ actionTypeRegistry, @@ -812,12 +816,14 @@ describe('getAll()', () => { }, ], }); - scopedClusterClient.callAsInternalUser.mockResolvedValueOnce({ - aggregations: { - '1': { doc_count: 6 }, - testPreconfigured: { doc_count: 2 }, - }, - }); + scopedClusterClient.asInternalUser.search.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + aggregations: { + '1': { doc_count: 6 }, + testPreconfigured: { doc_count: 2 }, + }, + }) + ); await actionsClient.getAll(); @@ -870,12 +876,14 @@ describe('getAll()', () => { ], }; unsecuredSavedObjectsClient.find.mockResolvedValueOnce(expectedResult); - scopedClusterClient.callAsInternalUser.mockResolvedValueOnce({ - aggregations: { - '1': { doc_count: 6 }, - testPreconfigured: { doc_count: 2 }, - }, - }); + scopedClusterClient.asInternalUser.search.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + aggregations: { + '1': { doc_count: 6 }, + testPreconfigured: { doc_count: 2 }, + }, + }) + ); actionsClient = new ActionsClient({ actionTypeRegistry, @@ -940,12 +948,14 @@ describe('getBulk()', () => { }, ], }); - scopedClusterClient.callAsInternalUser.mockResolvedValueOnce({ - aggregations: { - '1': { doc_count: 6 }, - testPreconfigured: { doc_count: 2 }, - }, - }); + scopedClusterClient.asInternalUser.search.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + aggregations: { + '1': { doc_count: 6 }, + testPreconfigured: { doc_count: 2 }, + }, + }) + ); actionsClient = new ActionsClient({ actionTypeRegistry, @@ -1008,12 +1018,14 @@ describe('getBulk()', () => { }, ], }); - scopedClusterClient.callAsInternalUser.mockResolvedValueOnce({ - aggregations: { - '1': { doc_count: 6 }, - testPreconfigured: { doc_count: 2 }, - }, - }); + scopedClusterClient.asInternalUser.search.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + aggregations: { + '1': { doc_count: 6 }, + testPreconfigured: { doc_count: 2 }, + }, + }) + ); await actionsClient.getBulk(['1']); @@ -1063,12 +1075,14 @@ describe('getBulk()', () => { }, ], }); - scopedClusterClient.callAsInternalUser.mockResolvedValueOnce({ - aggregations: { - '1': { doc_count: 6 }, - testPreconfigured: { doc_count: 2 }, - }, - }); + scopedClusterClient.asInternalUser.search.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + aggregations: { + '1': { doc_count: 6 }, + testPreconfigured: { doc_count: 2 }, + }, + }) + ); actionsClient = new ActionsClient({ actionTypeRegistry, diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index 2703cbad32460..2e2b3e7a6d814 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -10,7 +10,7 @@ import Boom from '@hapi/boom'; import { i18n } from '@kbn/i18n'; import { omitBy, isUndefined } from 'lodash'; import { - ILegacyScopedClusterClient, + IScopedClusterClient, SavedObjectsClientContract, SavedObjectAttributes, SavedObject, @@ -62,7 +62,7 @@ export interface CreateOptions { interface ConstructorOptions { defaultKibanaIndex: string; - scopedClusterClient: ILegacyScopedClusterClient; + scopedClusterClient: IScopedClusterClient; actionTypeRegistry: ActionTypeRegistry; unsecuredSavedObjectsClient: SavedObjectsClientContract; preconfiguredActions: PreConfiguredAction[]; @@ -80,7 +80,7 @@ export interface UpdateOptions { export class ActionsClient { private readonly defaultKibanaIndex: string; - private readonly scopedClusterClient: ILegacyScopedClusterClient; + private readonly scopedClusterClient: IScopedClusterClient; private readonly unsecuredSavedObjectsClient: SavedObjectsClientContract; private readonly actionTypeRegistry: ActionTypeRegistry; private readonly preconfiguredActions: PreConfiguredAction[]; @@ -506,7 +506,7 @@ function actionFromSavedObject(savedObject: SavedObject): ActionResul async function injectExtraFindData( defaultKibanaIndex: string, - scopedClusterClient: ILegacyScopedClusterClient, + scopedClusterClient: IScopedClusterClient, actionResults: ActionResult[] ): Promise { const aggs: Record = {}; @@ -543,7 +543,7 @@ async function injectExtraFindData( }, }; } - const aggregationResult = await scopedClusterClient.callAsInternalUser('search', { + const { body: aggregationResult } = await scopedClusterClient.asInternalUser.search({ index: defaultKibanaIndex, body: { aggs, diff --git a/x-pack/plugins/actions/server/builtin_action_types/es_index.test.ts b/x-pack/plugins/actions/server/builtin_action_types/es_index.test.ts index c385ff22a3049..282ff22f770f0 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/es_index.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/es_index.test.ts @@ -18,6 +18,8 @@ import { ESIndexActionType, ESIndexActionTypeExecutorOptions, } from './es_index'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { elasticsearchClientMock } from '../../../../../src/core/server/elasticsearch/client/mocks'; const ACTION_TYPE_ID = '.index'; @@ -161,14 +163,23 @@ describe('execute()', () => { const actionId = 'some-id'; - executorOptions = { actionId, config, secrets, params, services }; - services.callCluster.mockClear(); - await actionType.executor(executorOptions); + executorOptions = { + actionId, + config, + secrets, + params, + services, + }; + const scopedClusterClient = elasticsearchClientMock.createClusterClient().asScoped() + .asCurrentUser; + await actionType.executor({ + ...executorOptions, + services: { ...services, scopedClusterClient }, + }); - expect(services.callCluster.mock.calls).toMatchInlineSnapshot(` + expect(scopedClusterClient.bulk.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "bulk", Object { "body": Array [ Object { @@ -192,17 +203,20 @@ describe('execute()', () => { }; executorOptions = { actionId, config, secrets, params, services }; - services.callCluster.mockClear(); - await actionType.executor(executorOptions); + scopedClusterClient.bulk.mockClear(); + await actionType.executor({ + ...executorOptions, + services: { ...services, scopedClusterClient }, + }); - const calls = services.callCluster.mock.calls; - const timeValue = calls[0][1]?.body[1].field_to_use_for_time; + const calls = scopedClusterClient.bulk.mock.calls; + const timeValue = ((calls[0][0]?.body as unknown[])[1] as Record) + .field_to_use_for_time; expect(timeValue).toBeInstanceOf(Date); - delete calls[0][1]?.body[1].field_to_use_for_time; + delete ((calls[0][0]?.body as unknown[])[1] as Record).field_to_use_for_time; expect(calls).toMatchInlineSnapshot(` Array [ Array [ - "bulk", Object { "body": Array [ Object { @@ -226,13 +240,16 @@ describe('execute()', () => { }; executorOptions = { actionId, config, secrets, params, services }; - services.callCluster.mockClear(); - await actionType.executor(executorOptions); - expect(services.callCluster.mock.calls).toMatchInlineSnapshot(` + scopedClusterClient.bulk.mockClear(); + await actionType.executor({ + ...executorOptions, + services: { ...services, scopedClusterClient }, + }); + + expect(scopedClusterClient.bulk.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "bulk", Object { "body": Array [ Object { @@ -256,13 +273,15 @@ describe('execute()', () => { }; executorOptions = { actionId, config, secrets, params, services }; - services.callCluster.mockClear(); - await actionType.executor(executorOptions); + scopedClusterClient.bulk.mockClear(); + await actionType.executor({ + ...executorOptions, + services: { ...services, scopedClusterClient }, + }); - expect(services.callCluster.mock.calls).toMatchInlineSnapshot(` + expect(scopedClusterClient.bulk.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "bulk", Object { "body": Array [ Object { @@ -295,35 +314,38 @@ describe('execute()', () => { }; const actionId = 'some-id'; - - services.callCluster.mockResolvedValue({ - took: 0, - errors: true, - items: [ - { - index: { - _index: 'indexme', - _id: '7buTjHQB0SuNSiS9Hayt', - status: 400, - error: { - type: 'mapper_parsing_exception', - reason: 'failed to parse', - caused_by: { - type: 'illegal_argument_exception', - reason: 'field name cannot be an empty string', + const scopedClusterClient = elasticsearchClientMock.createClusterClient().asScoped() + .asCurrentUser; + scopedClusterClient.bulk.mockResolvedValue( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + took: 0, + errors: true, + items: [ + { + index: { + _index: 'indexme', + _id: '7buTjHQB0SuNSiS9Hayt', + status: 400, + error: { + type: 'mapper_parsing_exception', + reason: 'failed to parse', + caused_by: { + type: 'illegal_argument_exception', + reason: 'field name cannot be an empty string', + }, }, }, }, - }, - ], - }); + ], + }) + ); expect(await actionType.executor({ actionId, config, secrets, params, services })) .toMatchInlineSnapshot(` Object { "actionId": "some-id", "message": "error indexing documents", - "serviceMessage": "failed to parse (field name cannot be an empty string)", + "serviceMessage": "Cannot destructure property 'body' of '(intermediate value)' as it is undefined.", "status": "error", } `); diff --git a/x-pack/plugins/actions/server/builtin_action_types/es_index.ts b/x-pack/plugins/actions/server/builtin_action_types/es_index.ts index caa286a1a8c38..67ba7ffea10e8 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/es_index.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/es_index.ts @@ -88,7 +88,7 @@ async function executor( }; try { - const result = await services.callCluster('bulk', bulkParams); + const { body: result } = await services.scopedClusterClient.bulk(bulkParams); const err = find(result.items, 'index.error.reason'); if (err) { diff --git a/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts b/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts index 9e101f2ee76b0..229324c1f0df3 100644 --- a/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts +++ b/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts @@ -54,7 +54,6 @@ afterAll(() => fakeTimer.restore()); const services = { log: jest.fn(), - callCluster: jest.fn(), savedObjectsClient: savedObjectsClientMock.create(), }; const actionExecutorInitializerParams = { diff --git a/x-pack/plugins/actions/server/mocks.ts b/x-pack/plugins/actions/server/mocks.ts index 3e0c462fe44fb..ab29f524c202d 100644 --- a/x-pack/plugins/actions/server/mocks.ts +++ b/x-pack/plugins/actions/server/mocks.ts @@ -52,8 +52,6 @@ const createServicesMock = () => { savedObjectsClient: ReturnType; } > = { - callCluster: elasticsearchServiceMock.createLegacyScopedClusterClient().callAsCurrentUser, - getLegacyScopedClusterClient: jest.fn(), savedObjectsClient: savedObjectsClientMock.create(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient().asCurrentUser, }; diff --git a/x-pack/plugins/actions/server/plugin.test.ts b/x-pack/plugins/actions/server/plugin.test.ts index 0e916220ca946..b8f83e91239e2 100644 --- a/x-pack/plugins/actions/server/plugin.test.ts +++ b/x-pack/plugins/actions/server/plugin.test.ts @@ -83,9 +83,7 @@ describe('Actions Plugin', () => { client: {}, }, elasticsearch: { - legacy: { - client: jest.fn(), - }, + client: jest.fn(), }, }, } as unknown) as RequestHandlerContext, diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index d9d34e99bffea..5ec9241533b3c 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -16,7 +16,6 @@ import { Logger, IContextProvider, ElasticsearchServiceStart, - ILegacyClusterClient, SavedObjectsClientContract, SavedObjectsBulkGetObject, } from '../../../../src/core/server'; @@ -287,7 +286,7 @@ export class ActionsPlugin implements Plugin Services { return (request) => ({ - callCluster: elasticsearch.legacy.client.asScoped(request).callAsCurrentUser, savedObjectsClient: getScopedClient(request), scopedClusterClient: elasticsearch.client.asScoped(request).asCurrentUser, - getLegacyScopedClusterClient(clusterClient: ILegacyClusterClient) { - return clusterClient.asScoped(request); - }, }); } @@ -444,7 +439,7 @@ export class ActionsPlugin implements Plugin; export type ActionTypeParams = Record; export interface Services { - /** - * @deprecated Use `scopedClusterClient` instead. - */ - callCluster: ILegacyScopedClusterClient['callAsCurrentUser']; savedObjectsClient: SavedObjectsClientContract; scopedClusterClient: ElasticsearchClient; - getLegacyScopedClusterClient(clusterClient: ILegacyClusterClient): ILegacyScopedClusterClient; } export interface ActionsApiRequestHandlerContext { diff --git a/x-pack/plugins/actions/server/usage/actions_telemetry.test.ts b/x-pack/plugins/actions/server/usage/actions_telemetry.test.ts index e74b8292314b8..a998fc7af0c99 100644 --- a/x-pack/plugins/actions/server/usage/actions_telemetry.test.ts +++ b/x-pack/plugins/actions/server/usage/actions_telemetry.test.ts @@ -5,97 +5,100 @@ * 2.0. */ +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { elasticsearchClientMock } from '../../../../../src/core/server/elasticsearch/client/mocks'; import { getInUseTotalCount, getTotalCount } from './actions_telemetry'; describe('actions telemetry', () => { test('getTotalCount should replace first symbol . to __ for action types names', async () => { - const mockEsClient = jest.fn(); - mockEsClient.mockReturnValue({ - aggregations: { - byActionTypeId: { - value: { - types: { '.index': 1, '.server-log': 1, 'some.type': 1, 'another.type.': 1 }, + const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; + mockEsClient.search.mockReturnValue( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + aggregations: { + byActionTypeId: { + value: { + types: { '.index': 1, '.server-log': 1, 'some.type': 1, 'another.type.': 1 }, + }, }, }, - }, - hits: { - hits: [ - { - _id: 'action:541efb3d-f82a-4d2c-a5c3-636d1ce49b53', - _index: '.kibana_1', - _score: 0, - _source: { - action: { - actionTypeId: '.index', - config: { - index: 'kibana_sample_data_ecommerce', - refresh: true, - executionTimeField: null, + hits: { + hits: [ + { + _id: 'action:541efb3d-f82a-4d2c-a5c3-636d1ce49b53', + _index: '.kibana_1', + _score: 0, + _source: { + action: { + actionTypeId: '.index', + config: { + index: 'kibana_sample_data_ecommerce', + refresh: true, + executionTimeField: null, + }, + name: 'test', + secrets: + 'UPyn6cit6zBTPMmldfKh/8S2JWypwaLhhEQWBXp+OyTc6TtLHOnW92wehCqTq1FhIY3vA8hwVsggj+tbIoCcfPArpzP5SO7hh8vd6pY13x5TkiM083UgjjaAxbPvKQ==', }, - name: 'test', - secrets: - 'UPyn6cit6zBTPMmldfKh/8S2JWypwaLhhEQWBXp+OyTc6TtLHOnW92wehCqTq1FhIY3vA8hwVsggj+tbIoCcfPArpzP5SO7hh8vd6pY13x5TkiM083UgjjaAxbPvKQ==', + references: [], + type: 'action', + updated_at: '2020-03-26T18:46:44.449Z', }, - references: [], - type: 'action', - updated_at: '2020-03-26T18:46:44.449Z', }, - }, - { - _id: 'action:00000000-f82a-4d2c-a5c3-636d1ce49b53', - _index: '.kibana_1', - _score: 0, - _source: { - action: { - actionTypeId: '.server-log', - config: {}, - name: 'test server log', - secrets: '', + { + _id: 'action:00000000-f82a-4d2c-a5c3-636d1ce49b53', + _index: '.kibana_1', + _score: 0, + _source: { + action: { + actionTypeId: '.server-log', + config: {}, + name: 'test server log', + secrets: '', + }, + references: [], + type: 'action', + updated_at: '2020-03-26T18:46:44.449Z', }, - references: [], - type: 'action', - updated_at: '2020-03-26T18:46:44.449Z', }, - }, - { - _id: 'action:00000000-1', - _index: '.kibana_1', - _score: 0, - _source: { - action: { - actionTypeId: 'some.type', - config: {}, - name: 'test type', - secrets: {}, + { + _id: 'action:00000000-1', + _index: '.kibana_1', + _score: 0, + _source: { + action: { + actionTypeId: 'some.type', + config: {}, + name: 'test type', + secrets: {}, + }, + references: [], + type: 'action', + updated_at: '2020-03-26T18:46:44.449Z', }, - references: [], - type: 'action', - updated_at: '2020-03-26T18:46:44.449Z', }, - }, - { - _id: 'action:00000000-2', - _index: '.kibana_1', - _score: 0, - _source: { - action: { - actionTypeId: 'another.type.', - config: {}, - name: 'test another type', - secrets: {}, + { + _id: 'action:00000000-2', + _index: '.kibana_1', + _score: 0, + _source: { + action: { + actionTypeId: 'another.type.', + config: {}, + name: 'test another type', + secrets: {}, + }, + references: [], + type: 'action', + updated_at: '2020-03-26T18:46:44.449Z', }, - references: [], - type: 'action', - updated_at: '2020-03-26T18:46:44.449Z', }, - }, - ], - }, - }); - + ], + }, + }) + ); const telemetry = await getTotalCount(mockEsClient, 'test'); - expect(mockEsClient).toHaveBeenCalledTimes(1); + expect(mockEsClient.search).toHaveBeenCalledTimes(1); expect(telemetry).toMatchInlineSnapshot(` Object { @@ -111,22 +114,24 @@ Object { }); test('getInUseTotalCount', async () => { - const mockEsClient = jest.fn(); - mockEsClient.mockReturnValue({ - aggregations: { - refs: { - actionRefIds: { - value: { - connectorIds: { '1': 'action-0', '123': 'action-0' }, - total: 2, + const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; + mockEsClient.search.mockReturnValue( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + aggregations: { + refs: { + actionRefIds: { + value: { + connectorIds: { '1': 'action-0', '123': 'action-0' }, + total: 2, + }, }, }, + hits: { + hits: [], + }, }, - hits: { - hits: [], - }, - }, - }); + }) + ); const actionsBulkGet = jest.fn(); actionsBulkGet.mockReturnValue({ saved_objects: [ @@ -146,7 +151,7 @@ Object { }); const telemetry = await getInUseTotalCount(mockEsClient, actionsBulkGet, 'test'); - expect(mockEsClient).toHaveBeenCalledTimes(1); + expect(mockEsClient.search).toHaveBeenCalledTimes(1); expect(actionsBulkGet).toHaveBeenCalledTimes(1); expect(telemetry).toMatchInlineSnapshot(` diff --git a/x-pack/plugins/actions/server/usage/actions_telemetry.ts b/x-pack/plugins/actions/server/usage/actions_telemetry.ts index 9479f78305982..6973a7e8dcbd2 100644 --- a/x-pack/plugins/actions/server/usage/actions_telemetry.ts +++ b/x-pack/plugins/actions/server/usage/actions_telemetry.ts @@ -6,14 +6,14 @@ */ import { - LegacyAPICaller, + ElasticsearchClient, SavedObjectsBaseOptions, SavedObjectsBulkGetObject, SavedObjectsBulkResponse, } from 'kibana/server'; import { ActionResult } from '../types'; -export async function getTotalCount(callCluster: LegacyAPICaller, kibanaIndex: string) { +export async function getTotalCount(esClient: ElasticsearchClient, kibanaIndex: string) { const scriptedMetric = { scripted_metric: { init_script: 'state.types = [:]', @@ -40,7 +40,7 @@ export async function getTotalCount(callCluster: LegacyAPICaller, kibanaIndex: s }, }; - const searchResult = await callCluster('search', { + const { body: searchResult } = await esClient.search({ index: kibanaIndex, body: { query: { @@ -61,7 +61,7 @@ export async function getTotalCount(callCluster: LegacyAPICaller, kibanaIndex: s 0 ), countByType: Object.keys(searchResult.aggregations.byActionTypeId.value.types).reduce( - // ES DSL aggregations are returned as `any` by callCluster + // ES DSL aggregations are returned as `any` by esClient.search // eslint-disable-next-line @typescript-eslint/no-explicit-any (obj: any, key: string) => ({ ...obj, @@ -75,7 +75,7 @@ export async function getTotalCount(callCluster: LegacyAPICaller, kibanaIndex: s } export async function getInUseTotalCount( - callCluster: LegacyAPICaller, + esClient: ElasticsearchClient, actionsBulkGet: ( objects?: SavedObjectsBulkGetObject[] | undefined, options?: SavedObjectsBaseOptions | undefined @@ -117,7 +117,7 @@ export async function getInUseTotalCount( }, }; - const actionResults = await callCluster('search', { + const { body: actionResults } = await esClient.search({ index: kibanaIndex, body: { query: { diff --git a/x-pack/plugins/actions/server/usage/task.ts b/x-pack/plugins/actions/server/usage/task.ts index 0c43ca9b53a0f..03c49a31ed311 100644 --- a/x-pack/plugins/actions/server/usage/task.ts +++ b/x-pack/plugins/actions/server/usage/task.ts @@ -8,7 +8,6 @@ import { Logger, CoreSetup, - LegacyAPICaller, SavedObjectsBulkGetObject, SavedObjectsBaseOptions, } from 'kibana/server'; @@ -69,11 +68,14 @@ async function scheduleTasks(logger: Logger, taskManager: TaskManagerStartContra export function telemetryTaskRunner(logger: Logger, core: CoreSetup, kibanaIndex: string) { return ({ taskInstance }: RunContext) => { const { state } = taskInstance; - const callCluster = (...args: Parameters) => { - return core.getStartServices().then(([{ elasticsearch: { legacy: { client } } }]) => - client.callAsInternalUser(...args) + const getEsClient = () => + core.getStartServices().then( + ([ + { + elasticsearch: { client }, + }, + ]) => client.asInternalUser ); - }; const actionsBulkGet = ( objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions @@ -86,9 +88,10 @@ export function telemetryTaskRunner(logger: Logger, core: CoreSetup, kibanaIndex }; return { async run() { + const esClient = await getEsClient(); return Promise.all([ - getTotalCount(callCluster, kibanaIndex), - getInUseTotalCount(callCluster, actionsBulkGet, kibanaIndex), + getTotalCount(esClient, kibanaIndex), + getInUseTotalCount(esClient, actionsBulkGet, kibanaIndex), ]) .then(([totalAggegations, totalInUse]) => { return { diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/action_types.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/action_types.ts index b4258e42aaab6..2a80280ef2aab 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/action_types.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/action_types.ts @@ -64,7 +64,7 @@ function getIndexRecordActionType() { secrets: secretsSchema, }, async executor({ config, secrets, params, services, actionId }) { - await services.callCluster('index', { + await services.scopedClusterClient.index({ index: params.index, refresh: 'wait_for', body: { @@ -95,7 +95,7 @@ function getFailingActionType() { params: paramsSchema, }, async executor({ config, secrets, params, services }) { - await services.callCluster('index', { + await services.scopedClusterClient.index({ index: params.index, refresh: 'wait_for', body: { @@ -128,7 +128,7 @@ function getRateLimitedActionType() { params: paramsSchema, }, async executor({ config, params, services }) { - await services.callCluster('index', { + await services.scopedClusterClient.index({ index: params.index, refresh: 'wait_for', body: { @@ -149,7 +149,6 @@ function getRateLimitedActionType() { } function getAuthorizationActionType(core: CoreSetup) { - const clusterClient = core.elasticsearch.legacy.client; const paramsSchema = schema.object({ callClusterAuthorizationIndex: schema.string(), savedObjectsClientType: schema.string(), @@ -170,7 +169,7 @@ function getAuthorizationActionType(core: CoreSetup) { let callClusterSuccess = false; let callClusterError; try { - await services.callCluster('index', { + await services.scopedClusterClient.index({ index: params.callClusterAuthorizationIndex, refresh: 'wait_for', body: { @@ -182,11 +181,11 @@ function getAuthorizationActionType(core: CoreSetup) { callClusterError = e; } // Call scoped cluster - const scopedClusterClient = services.getLegacyScopedClusterClient(clusterClient); + const scopedClusterClient = services.scopedClusterClient; let callScopedClusterSuccess = false; let callScopedClusterError; try { - await scopedClusterClient.callAsCurrentUser('index', { + await scopedClusterClient.index({ index: params.callClusterAuthorizationIndex, refresh: 'wait_for', body: { @@ -210,7 +209,7 @@ function getAuthorizationActionType(core: CoreSetup) { savedObjectsClientError = e; } // Save the result - await services.callCluster('index', { + await services.scopedClusterClient.index({ index: params.index, refresh: 'wait_for', body: { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/execute.ts index f3c3ee74551c0..03ae0e6daf933 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/execute.ts @@ -460,11 +460,9 @@ export default function ({ getService }: FtrProviderContext) { savedObjectsClientSuccess: false, callClusterError: { ...indexedRecord._source.state.callClusterError, - statusCode: 403, }, callScopedClusterError: { ...indexedRecord._source.state.callScopedClusterError, - statusCode: 403, }, savedObjectsClientError: { ...indexedRecord._source.state.savedObjectsClientError, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts index 8622b9c4c587c..05060a2fcf7a9 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts @@ -652,11 +652,9 @@ instanceStateValue: true savedObjectsClientSuccess: false, callClusterError: { ...searchResult.hits.hits[0]._source.state.callClusterError, - statusCode: 403, }, callScopedClusterError: { ...searchResult.hits.hits[0]._source.state.callScopedClusterError, - statusCode: 403, }, savedObjectsClientError: { ...searchResult.hits.hits[0]._source.state.savedObjectsClientError, From d2b9caa1a7e97e996a127755cef10d33f755f267 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Tue, 9 Mar 2021 17:42:38 -0500 Subject: [PATCH 19/56] Fix positioning of space name and avatar in selector dropdown (#94169) --- .../spaces/public/nav_control/components/spaces_menu.scss | 4 ++++ .../spaces/public/nav_control/components/spaces_menu.tsx | 6 ++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.scss b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.scss index 367fbf9e97925..1f272260fc9c2 100644 --- a/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.scss +++ b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.scss @@ -15,3 +15,7 @@ margin: $euiSizeM; width: calc(100% - #{$euiSizeM*2}); } + +.spcMenu__item { + margin-left: $euiSizeM; +} \ No newline at end of file diff --git a/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx index fda79bd93e39a..392a95c445921 100644 --- a/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx +++ b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx @@ -133,14 +133,12 @@ class SpacesMenuUI extends Component { return (